diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index 6c0ee435b5e20..db022ae7f0f58 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -353,7 +353,9 @@ enabled: - x-pack/test/security_solution_endpoint/config.ts - x-pack/test/session_view/basic/config.ts - x-pack/test/spaces_api_integration/security_and_spaces/config_basic.ts + - x-pack/test/spaces_api_integration/security_and_spaces/copy_to_space_config_basic.ts - x-pack/test/spaces_api_integration/security_and_spaces/config_trial.ts + - x-pack/test/spaces_api_integration/security_and_spaces/copy_to_space_config_trial.ts - x-pack/test/spaces_api_integration/spaces_only/config.ts - x-pack/test/timeline/security_and_spaces/config_trial.ts - x-pack/test/ui_capabilities/security_and_spaces/config.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index bfe6716b0d2c0..cb9607ecbb51a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -459,6 +459,7 @@ x-pack/examples/third_party_maps_source_example @elastic/kibana-gis src/plugins/maps_ems @elastic/kibana-gis x-pack/plugins/maps @elastic/kibana-gis x-pack/packages/ml/agg_utils @elastic/ml-ui +x-pack/packages/ml/anomaly_utils @elastic/ml-ui x-pack/packages/ml/date_picker @elastic/ml-ui x-pack/packages/ml/error_utils @elastic/ml-ui x-pack/packages/ml/is_defined @elastic/ml-ui @@ -578,6 +579,7 @@ x-pack/plugins/serverless @elastic/appex-sharedux x-pack/plugins/serverless_observability @elastic/appex-sharedux packages/serverless/project_switcher @elastic/appex-sharedux x-pack/plugins/serverless_search @elastic/appex-sharedux +x-pack/plugins/serverless_security @elastic/appex-sharedux packages/serverless/storybook/config @elastic/appex-sharedux packages/serverless/types @elastic/appex-sharedux test/plugin_functional/plugins/session_notifications @elastic/kibana-core diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 643f8f398a277..62f018e08063b 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github description: API docs for the actions plugin -date: 2023-05-02 +date: 2023-05-03 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 e510f0ae54fb9..7ddec2f57e2dd 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github description: API docs for the advancedSettings plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index 36ccb8faa79df..1492459ea7ccb 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github description: API docs for the aiops plugin -date: 2023-05-02 +date: 2023-05-03 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 1f82f80e78167..a20d8bd4fd6db 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github description: API docs for the alerting plugin -date: 2023-05-02 +date: 2023-05-03 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 c69e04ef3ff29..a47cc0a320a96 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github description: API docs for the apm plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/asset_manager.mdx b/api_docs/asset_manager.mdx index e04d4227658e1..d2f41ffaa2eb6 100644 --- a/api_docs/asset_manager.mdx +++ b/api_docs/asset_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/assetManager title: "assetManager" image: https://source.unsplash.com/400x175/?github description: API docs for the assetManager plugin -date: 2023-05-02 +date: 2023-05-03 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 d6fe1959d500f..8eccc9190f351 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github description: API docs for the banners plugin -date: 2023-05-02 +date: 2023-05-03 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 23e5c705233c2..739cd25f31dff 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github description: API docs for the bfetch plugin -date: 2023-05-02 +date: 2023-05-03 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 976fe3b2dcb19..6101a48463628 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github description: API docs for the canvas plugin -date: 2023-05-02 +date: 2023-05-03 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 bc5d0a0b2f284..1277a68e5d9dd 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github description: API docs for the cases plugin -date: 2023-05-02 +date: 2023-05-03 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 3a7c72cbb978e..83084ea8685fa 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github description: API docs for the charts plugin -date: 2023-05-02 +date: 2023-05-03 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 072b91029ba75..e2765dff788db 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github description: API docs for the cloud plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_chat.mdx b/api_docs/cloud_chat.mdx index 53c89b2f2bb54..0a96e740a19d5 100644 --- a/api_docs/cloud_chat.mdx +++ b/api_docs/cloud_chat.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudChat title: "cloudChat" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudChat plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudChat'] --- import cloudChatObj from './cloud_chat.devdocs.json'; diff --git a/api_docs/cloud_data_migration.mdx b/api_docs/cloud_data_migration.mdx index 86a01340c0805..24e5ad230baab 100644 --- a/api_docs/cloud_data_migration.mdx +++ b/api_docs/cloud_data_migration.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDataMigration title: "cloudDataMigration" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDataMigration plugin -date: 2023-05-02 +date: 2023-05-03 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 7d1a207077719..fc15a161f5734 100644 --- a/api_docs/cloud_defend.mdx +++ b/api_docs/cloud_defend.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDefend title: "cloudDefend" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDefend plugin -date: 2023-05-02 +date: 2023-05-03 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 3373194ec297d..1fb43a7863d7a 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2023-05-02 +date: 2023-05-03 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 76c7f82798ab1..5d5d163d6a868 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudSecurityPosture plugin -date: 2023-05-02 +date: 2023-05-03 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 4ec47966bf7e0..3e08be7d258c1 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github description: API docs for the console plugin -date: 2023-05-02 +date: 2023-05-03 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 5a90a9d17c5b5..aea72d4e1e973 100644 --- a/api_docs/content_management.mdx +++ b/api_docs/content_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/contentManagement title: "contentManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the contentManagement plugin -date: 2023-05-02 +date: 2023-05-03 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 5bffc384f47f5..567c3ee173b3d 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github description: API docs for the controls plugin -date: 2023-05-02 +date: 2023-05-03 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 fc41831aa1424..bf92721582147 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github description: API docs for the customIntegrations plugin -date: 2023-05-02 +date: 2023-05-03 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 905ff061506b9..3d9820e27e483 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboard plugin -date: 2023-05-02 +date: 2023-05-03 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 c263e3c24a6ca..37465df69ae81 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboardEnhanced plugin -date: 2023-05-02 +date: 2023-05-03 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 76b8aadb27ea6..f97231a98b47c 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github description: API docs for the data plugin -date: 2023-05-02 +date: 2023-05-03 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 dfadcaf7262d6..66614407e92f1 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github description: API docs for the data.query plugin -date: 2023-05-02 +date: 2023-05-03 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 bef70c74b15ca..7ca65f211105c 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github description: API docs for the data.search plugin -date: 2023-05-02 +date: 2023-05-03 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 363757369f433..44492a6054937 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewEditor plugin -date: 2023-05-02 +date: 2023-05-03 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 e059675d22779..cdda39c638b75 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewFieldEditor plugin -date: 2023-05-02 +date: 2023-05-03 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 b6d17c3bf9a35..3db079a469c70 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewManagement plugin -date: 2023-05-02 +date: 2023-05-03 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 9d22dcbd0359e..30e066763a528 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViews plugin -date: 2023-05-02 +date: 2023-05-03 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 c788971320cf6..160671b16fa1d 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github description: API docs for the dataVisualizer plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index e56d751d35cdb..e0b32a130df63 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 12cf65976c0c2..fb68f0aaa374c 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -861,7 +861,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [loader.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/data_views_service/loader.ts#:~:text=title), [lens_top_nav.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx#:~:text=title), [loader.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/data_views_service/loader.ts#:~:text=title), [lens_top_nav.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx#:~:text=title) | - | | | [loader.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/data_views_service/loader.ts#:~:text=title), [lens_top_nav.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx#:~:text=title), [loader.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/data_views_service/loader.ts#:~:text=title), [lens_top_nav.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx#:~:text=title) | - | | | [loader.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/data_views_service/loader.ts#:~:text=title), [lens_top_nav.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx#:~:text=title) | - | -| | [lens_attribute_service.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/lens_attribute_service.ts#:~:text=savedObjects), [lens_attribute_service.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/lens_attribute_service.ts#:~:text=savedObjects), [form_based.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx#:~:text=savedObjects), [form_based.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx#:~:text=savedObjects), [form_based.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx#:~:text=savedObjects), [visualization.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx#:~:text=savedObjects), [visualization.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx#:~:text=savedObjects), [mounter.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/app_plugin/mounter.tsx#:~:text=savedObjects) | - | +| | [lens_attribute_service.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/lens_attribute_service.ts#:~:text=savedObjects), [lens_attribute_service.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/lens_attribute_service.ts#:~:text=savedObjects), [form_based.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx#:~:text=savedObjects), [form_based.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx#:~:text=savedObjects), [form_based.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx#:~:text=savedObjects), [visualization.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx#:~:text=savedObjects), [visualization.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx#:~:text=savedObjects), [visualization.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx#:~:text=savedObjects), [mounter.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/app_plugin/mounter.tsx#:~:text=savedObjects) | - | | | [find_object_by_title.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/persistence/saved_objects_utils/find_object_by_title.ts#:~:text=SavedObjectsClientContract), [find_object_by_title.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/persistence/saved_objects_utils/find_object_by_title.ts#:~:text=SavedObjectsClientContract), [check_for_duplicate_title.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/persistence/saved_objects_utils/check_for_duplicate_title.ts#:~:text=SavedObjectsClientContract), [check_for_duplicate_title.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/persistence/saved_objects_utils/check_for_duplicate_title.ts#:~:text=SavedObjectsClientContract), [reference_editor.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/reference_editor.tsx#:~:text=SavedObjectsClientContract), [reference_editor.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/reference_editor.tsx#:~:text=SavedObjectsClientContract), [dimension_panel.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/dimension_panel.tsx#:~:text=SavedObjectsClientContract), [dimension_panel.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/dimension_panel.tsx#:~:text=SavedObjectsClientContract), [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/index.ts#:~:text=SavedObjectsClientContract), [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/index.ts#:~:text=SavedObjectsClientContract)+ 8 more | - | | | [saved_object_store.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/persistence/saved_object_store.ts#:~:text=create) | - | | | [find_object_by_title.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/persistence/saved_objects_utils/find_object_by_title.ts#:~:text=find), [find_object_by_title.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/persistence/saved_objects_utils/find_object_by_title.test.ts#:~:text=find), [find_object_by_title.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/lens/public/persistence/saved_objects_utils/find_object_by_title.test.ts#:~:text=find) | - | diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 644fc63e30ecd..bc6e3ad8d1291 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team description: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index a132aa30c0975..73759c4b9e15c 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github description: API docs for the devTools plugin -date: 2023-05-02 +date: 2023-05-03 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 8204782e959f0..eb130a50d7992 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github description: API docs for the discover plugin -date: 2023-05-02 +date: 2023-05-03 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 9703436556399..4c3153f5590b6 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverEnhanced plugin -date: 2023-05-02 +date: 2023-05-03 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 f849c25f91cfb..27e713e7a085f 100644 --- a/api_docs/ecs_data_quality_dashboard.mdx +++ b/api_docs/ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ecsDataQualityDashboard title: "ecsDataQualityDashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the ecsDataQualityDashboard plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ecsDataQualityDashboard'] --- import ecsDataQualityDashboardObj from './ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index fb38acacac597..27510d5fa8bc9 100644 --- a/api_docs/embeddable.mdx +++ b/api_docs/embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddable title: "embeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddable plugin -date: 2023-05-02 +date: 2023-05-03 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 1f703de01c498..382b09177ef5a 100644 --- a/api_docs/embeddable_enhanced.mdx +++ b/api_docs/embeddable_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced title: "embeddableEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddableEnhanced plugin -date: 2023-05-02 +date: 2023-05-03 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 0c89158340f04..6741f03c9a527 100644 --- a/api_docs/encrypted_saved_objects.mdx +++ b/api_docs/encrypted_saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects title: "encryptedSavedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the encryptedSavedObjects plugin -date: 2023-05-02 +date: 2023-05-03 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 95ecf0488db87..bc1a7ad6319c3 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the enterpriseSearch plugin -date: 2023-05-02 +date: 2023-05-03 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 107590a9fe1d0..1c7cb0a4a737c 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github description: API docs for the esUiShared plugin -date: 2023-05-02 +date: 2023-05-03 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 a96d0ba058b9a..b9995e81f7396 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotation plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index a47477d55e2c1..06ab3d2458859 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github description: API docs for the eventLog plugin -date: 2023-05-02 +date: 2023-05-03 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 49ef9d49f9b01..8c10bbc4b3236 100644 --- a/api_docs/exploratory_view.mdx +++ b/api_docs/exploratory_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/exploratoryView title: "exploratoryView" image: https://source.unsplash.com/400x175/?github description: API docs for the exploratoryView plugin -date: 2023-05-02 +date: 2023-05-03 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 ddb8db1841bb1..d8c21a9d85a7d 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionError plugin -date: 2023-05-02 +date: 2023-05-03 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 8d8815651a322..d8e0f495629b0 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionGauge plugin -date: 2023-05-02 +date: 2023-05-03 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 dd955e8ff05c3..7df7fe980d53c 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionHeatmap plugin -date: 2023-05-02 +date: 2023-05-03 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 36cbbd3eb65b6..204a2bf9584ed 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionImage plugin -date: 2023-05-02 +date: 2023-05-03 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 bc5cb5c8746b5..69eec7ebd4773 100644 --- a/api_docs/expression_legacy_metric_vis.mdx +++ b/api_docs/expression_legacy_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionLegacyMetricVis title: "expressionLegacyMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionLegacyMetricVis plugin -date: 2023-05-02 +date: 2023-05-03 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 06c68986a7d34..18a2d8df875c8 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetric plugin -date: 2023-05-02 +date: 2023-05-03 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 92f6942df0da8..6d9e9cbfdf8f2 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetricVis plugin -date: 2023-05-02 +date: 2023-05-03 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 a23242c6af6f2..ff152c343b46a 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionPartitionVis plugin -date: 2023-05-02 +date: 2023-05-03 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 4daa6a8efcf6d..05e53c7f58490 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRepeatImage plugin -date: 2023-05-02 +date: 2023-05-03 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 754f072c65bb8..cecf10c743a4d 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRevealImage plugin -date: 2023-05-02 +date: 2023-05-03 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 0c6ffeb32e11b..30cf33e6066d1 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionShape plugin -date: 2023-05-02 +date: 2023-05-03 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 dc26652c957dc..c059bca82026b 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionTagcloud plugin -date: 2023-05-02 +date: 2023-05-03 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 8df55307fdc58..111520c8d3e96 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionXY plugin -date: 2023-05-02 +date: 2023-05-03 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 40fe6d96de372..3c0f481f71273 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github description: API docs for the expressions plugin -date: 2023-05-02 +date: 2023-05-03 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 d95898ceaaccd..7fd52fdb1e62b 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github description: API docs for the features plugin -date: 2023-05-02 +date: 2023-05-03 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 3e52527f05645..4472b1454f185 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldFormats plugin -date: 2023-05-02 +date: 2023-05-03 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 93498744902af..494b140b5a9d5 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github description: API docs for the fileUpload plugin -date: 2023-05-02 +date: 2023-05-03 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 1d00c75b51a2a..a997c1b76a165 100644 --- a/api_docs/files.mdx +++ b/api_docs/files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/files title: "files" image: https://source.unsplash.com/400x175/?github description: API docs for the files plugin -date: 2023-05-02 +date: 2023-05-03 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 fc635ded398d3..0fdb228526124 100644 --- a/api_docs/files_management.mdx +++ b/api_docs/files_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/filesManagement title: "filesManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the filesManagement plugin -date: 2023-05-02 +date: 2023-05-03 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 a917afbaf7683..27b55593e87d5 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the fleet plugin -date: 2023-05-02 +date: 2023-05-03 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 426b271ca3618..7af6df1ca5a76 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the globalSearch plugin -date: 2023-05-02 +date: 2023-05-03 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 1f47d4d865fd2..f529fbd88f483 100644 --- a/api_docs/guided_onboarding.mdx +++ b/api_docs/guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/guidedOnboarding title: "guidedOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the guidedOnboarding plugin -date: 2023-05-02 +date: 2023-05-03 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 edfff5e479a0f..da5579adeba5f 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github description: API docs for the home plugin -date: 2023-05-02 +date: 2023-05-03 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 8e9fe1096d4e3..155c935921f63 100644 --- a/api_docs/image_embeddable.mdx +++ b/api_docs/image_embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/imageEmbeddable title: "imageEmbeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the imageEmbeddable plugin -date: 2023-05-02 +date: 2023-05-03 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 4fcb9e8d756ad..435e12ba7ff8e 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexLifecycleManagement plugin -date: 2023-05-02 +date: 2023-05-03 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 c71fbc1b28d08..fbae5bdf24303 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexManagement plugin -date: 2023-05-02 +date: 2023-05-03 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 f1afb04a532e0..02f681bcc38fe 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github description: API docs for the infra plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index 85718cf90e71e..70e89d7195207 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github description: API docs for the inspector plugin -date: 2023-05-02 +date: 2023-05-03 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 2b5f3539c8d2c..fdc96e1662cd5 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github description: API docs for the interactiveSetup plugin -date: 2023-05-02 +date: 2023-05-03 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 0243e0a3e62fe..a2f55ed9cb1ed 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index 71f5e26e56dd5..3e1e49197df4c 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-components plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx index 3ff47fa855480..d2db6678d020f 100644 --- a/api_docs/kbn_aiops_utils.mdx +++ b/api_docs/kbn_aiops_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils title: "@kbn/aiops-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-utils plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] --- import kbnAiopsUtilsObj from './kbn_aiops_utils.devdocs.json'; diff --git a/api_docs/kbn_alerting_state_types.mdx b/api_docs/kbn_alerting_state_types.mdx index bf3479cc2d6ba..284c355efc52f 100644 --- a/api_docs/kbn_alerting_state_types.mdx +++ b/api_docs/kbn_alerting_state_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-state-types title: "@kbn/alerting-state-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-state-types plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-state-types'] --- import kbnAlertingStateTypesObj from './kbn_alerting_state_types.devdocs.json'; diff --git a/api_docs/kbn_alerts.mdx b/api_docs/kbn_alerts.mdx index 283b328eb1e10..2aa59a79b9b98 100644 --- a/api_docs/kbn_alerts.mdx +++ b/api_docs/kbn_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts title: "@kbn/alerts" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts'] --- import kbnAlertsObj from './kbn_alerts.devdocs.json'; diff --git a/api_docs/kbn_alerts_as_data_utils.mdx b/api_docs/kbn_alerts_as_data_utils.mdx index 6f93eed297d40..f9589235de143 100644 --- a/api_docs/kbn_alerts_as_data_utils.mdx +++ b/api_docs/kbn_alerts_as_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-as-data-utils title: "@kbn/alerts-as-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-as-data-utils plugin -date: 2023-05-02 +date: 2023-05-03 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 cbae603dbc803..76be38521aa71 100644 --- a/api_docs/kbn_alerts_ui_shared.mdx +++ b/api_docs/kbn_alerts_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-ui-shared title: "@kbn/alerts-ui-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-ui-shared plugin -date: 2023-05-02 +date: 2023-05-03 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 41e181d4c6547..9709e92533a5a 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index e4991e5c1734c..67573ee099194 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index 72496b93d1fbe..293e11920a5c0 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2023-05-02 +date: 2023-05-03 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 30dbfc9a693d0..b2f9aaf252444 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2023-05-02 +date: 2023-05-03 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 f88667e32bc6a..6988aa2180e90 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2023-05-02 +date: 2023-05-03 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 9e77ba0027e9b..e73c765187aa1 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_gainsight.mdx b/api_docs/kbn_analytics_shippers_gainsight.mdx index b71cf3d766fcf..25cff66a64cd3 100644 --- a/api_docs/kbn_analytics_shippers_gainsight.mdx +++ b/api_docs/kbn_analytics_shippers_gainsight.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-gainsight title: "@kbn/analytics-shippers-gainsight" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-gainsight plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-gainsight'] --- import kbnAnalyticsShippersGainsightObj from './kbn_analytics_shippers_gainsight.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 5aeba9e2c1236..cf987591c11a2 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-config-loader plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] --- import kbnApmConfigLoaderObj from './kbn_apm_config_loader.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index dff42f021f7ff..90a781efc9f94 100644 --- a/api_docs/kbn_apm_synthtrace.mdx +++ b/api_docs/kbn_apm_synthtrace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace title: "@kbn/apm-synthtrace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace plugin -date: 2023-05-02 +date: 2023-05-03 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 03d1e95bfaa9a..e8a935690c881 100644 --- a/api_docs/kbn_apm_synthtrace_client.mdx +++ b/api_docs/kbn_apm_synthtrace_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace-client title: "@kbn/apm-synthtrace-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace-client plugin -date: 2023-05-02 +date: 2023-05-03 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 4797f5606558b..71b7bce36495d 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-utils plugin -date: 2023-05-02 +date: 2023-05-03 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 a2e859bdddc7c..13db9eef60d32 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/axe-config plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_cases_components.mdx b/api_docs/kbn_cases_components.mdx index 4f6259398a3be..15cb368c1d12e 100644 --- a/api_docs/kbn_cases_components.mdx +++ b/api_docs/kbn_cases_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cases-components title: "@kbn/cases-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cases-components plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cases-components'] --- import kbnCasesComponentsObj from './kbn_cases_components.devdocs.json'; diff --git a/api_docs/kbn_cell_actions.devdocs.json b/api_docs/kbn_cell_actions.devdocs.json index e24dccb321638..2cafafac5fa41 100644 --- a/api_docs/kbn_cell_actions.devdocs.json +++ b/api_docs/kbn_cell_actions.devdocs.json @@ -1106,7 +1106,7 @@ "label": "fields", "description": [], "signature": [ - "BulkField[]" + "BulkField[] | undefined" ], "path": "packages/kbn-cell-actions/src/hooks/use_data_grid_column_cell_actions.tsx", "deprecated": false, diff --git a/api_docs/kbn_cell_actions.mdx b/api_docs/kbn_cell_actions.mdx index 89e48075f4dd0..f5c40cfbace49 100644 --- a/api_docs/kbn_cell_actions.mdx +++ b/api_docs/kbn_cell_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cell-actions title: "@kbn/cell-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cell-actions plugin -date: 2023-05-02 +date: 2023-05-03 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 f83fc50ad982f..a64c7b862b2c6 100644 --- a/api_docs/kbn_chart_expressions_common.mdx +++ b/api_docs/kbn_chart_expressions_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-expressions-common title: "@kbn/chart-expressions-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-expressions-common plugin -date: 2023-05-02 +date: 2023-05-03 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 7e96511c88bcd..e6e9aae56e4e2 100644 --- a/api_docs/kbn_chart_icons.mdx +++ b/api_docs/kbn_chart_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-icons title: "@kbn/chart-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-icons plugin -date: 2023-05-02 +date: 2023-05-03 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 ebdaa008c4162..2f1bef7620c94 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-core plugin -date: 2023-05-02 +date: 2023-05-03 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 235a83463c999..3390eed9c66b8 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2023-05-02 +date: 2023-05-03 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 e4499909164ed..28958beca139b 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-reporter plugin -date: 2023-05-02 +date: 2023-05-03 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 8aa10da29eb55..2326d37c3e97b 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cli-dev-mode plugin -date: 2023-05-02 +date: 2023-05-03 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 2e9c06092a4b4..3509b913518f0 100644 --- a/api_docs/kbn_code_editor.mdx +++ b/api_docs/kbn_code_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor title: "@kbn/code-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] --- import kbnCodeEditorObj from './kbn_code_editor.devdocs.json'; diff --git a/api_docs/kbn_code_editor_mocks.mdx b/api_docs/kbn_code_editor_mocks.mdx index add02989b7a3c..20fcb0fa56b34 100644 --- a/api_docs/kbn_code_editor_mocks.mdx +++ b/api_docs/kbn_code_editor_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor-mocks title: "@kbn/code-editor-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor-mocks plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor-mocks'] --- import kbnCodeEditorMocksObj from './kbn_code_editor_mocks.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index c64c84c1e20ea..a7f5279a6fb8d 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/coloring plugin -date: 2023-05-02 +date: 2023-05-03 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 70b1d1cf85bba..99efea0df4dcf 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config plugin -date: 2023-05-02 +date: 2023-05-03 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 e9abe9d53fc05..c053e552cff52 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 d4732941344d1..5a5acf77cf50b 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-schema plugin -date: 2023-05-02 +date: 2023-05-03 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 cd016ed79901c..2027a1d10520a 100644 --- a/api_docs/kbn_content_management_content_editor.mdx +++ b/api_docs/kbn_content_management_content_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-editor title: "@kbn/content-management-content-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-editor plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-editor'] --- import kbnContentManagementContentEditorObj from './kbn_content_management_content_editor.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list.mdx b/api_docs/kbn_content_management_table_list.mdx index d4cdeb4ae876e..f19b201be52f6 100644 --- a/api_docs/kbn_content_management_table_list.mdx +++ b/api_docs/kbn_content_management_table_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list title: "@kbn/content-management-table-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list'] --- import kbnContentManagementTableListObj from './kbn_content_management_table_list.devdocs.json'; diff --git a/api_docs/kbn_content_management_utils.mdx b/api_docs/kbn_content_management_utils.mdx index 6cede7fc76173..9c467807494b2 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: 2023-05-02 +date: 2023-05-03 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 02b6af211ced8..a5ca58476ecfa 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser plugin -date: 2023-05-02 +date: 2023-05-03 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 e57d4ad24be5f..8ea576d59580a 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 6a4a589466c5b..49ca962e54de7 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 fe46d712752e6..47be55e7e2ee5 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server plugin -date: 2023-05-02 +date: 2023-05-03 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 d4e9a73b9e680..4feb74f2fecba 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 835415c6ffe0e..34446c493a62f 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 e25a69c331c94..a719378fe8963 100644 --- a/api_docs/kbn_core_application_browser.mdx +++ b/api_docs/kbn_core_application_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser title: "@kbn/core-application-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser plugin -date: 2023-05-02 +date: 2023-05-03 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 6f009e8e9e043..9de2ca4244651 100644 --- a/api_docs/kbn_core_application_browser_internal.mdx +++ b/api_docs/kbn_core_application_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-internal title: "@kbn/core-application-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 848a546060ce6..adb64f33051ab 100644 --- a/api_docs/kbn_core_application_browser_mocks.mdx +++ b/api_docs/kbn_core_application_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-mocks title: "@kbn/core-application-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 115844c847afe..396bcd07b4b76 100644 --- a/api_docs/kbn_core_application_common.mdx +++ b/api_docs/kbn_core_application_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-common title: "@kbn/core-application-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-common plugin -date: 2023-05-02 +date: 2023-05-03 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 43c5d9efa5437..e494bc3a4f364 100644 --- a/api_docs/kbn_core_apps_browser_internal.mdx +++ b/api_docs/kbn_core_apps_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-internal title: "@kbn/core-apps-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 09956c520a50b..b239ba917856d 100644 --- a/api_docs/kbn_core_apps_browser_mocks.mdx +++ b/api_docs/kbn_core_apps_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-mocks title: "@kbn/core-apps-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 bfabcf21cd384..49f6c7f64b1c9 100644 --- a/api_docs/kbn_core_apps_server_internal.mdx +++ b/api_docs/kbn_core_apps_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-server-internal title: "@kbn/core-apps-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 65a1d90590a83..018fc70629081 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 60a0e625ec748..a304d0701b1f6 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-common plugin -date: 2023-05-02 +date: 2023-05-03 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 8b2067c4518b5..3f8f4ee6a2fab 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 cab43c438d76d..5ddf1b15e5682 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 6362d4af6852b..c40a5e28bc21d 100644 --- a/api_docs/kbn_core_capabilities_browser_mocks.mdx +++ b/api_docs/kbn_core_capabilities_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-browser-mocks title: "@kbn/core-capabilities-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 125ee7975da71..8ce40f8e03270 100644 --- a/api_docs/kbn_core_capabilities_common.mdx +++ b/api_docs/kbn_core_capabilities_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-common title: "@kbn/core-capabilities-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-common plugin -date: 2023-05-02 +date: 2023-05-03 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 4fb936f648b37..ce94816fb17fe 100644 --- a/api_docs/kbn_core_capabilities_server.mdx +++ b/api_docs/kbn_core_capabilities_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server title: "@kbn/core-capabilities-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server plugin -date: 2023-05-02 +date: 2023-05-03 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 1030c5e4e8d42..9675f9a6239ec 100644 --- a/api_docs/kbn_core_capabilities_server_mocks.mdx +++ b/api_docs/kbn_core_capabilities_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server-mocks title: "@kbn/core-capabilities-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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.devdocs.json b/api_docs/kbn_core_chrome_browser.devdocs.json index 04cae5a9ab495..b6a2c3c3a1a83 100644 --- a/api_docs/kbn_core_chrome_browser.devdocs.json +++ b/api_docs/kbn_core_chrome_browser.devdocs.json @@ -1327,6 +1327,132 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-common.ChromeProjectNavigation", + "type": "Interface", + "tags": [], + "label": "ChromeProjectNavigation", + "description": [], + "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-common.ChromeProjectNavigation.navigationTree", + "type": "Array", + "tags": [], + "label": "navigationTree", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-chrome-browser", + "scope": "common", + "docId": "kibKbnCoreChromeBrowserPluginApi", + "section": "def-common.ChromeProjectNavigationNode", + "text": "ChromeProjectNavigationNode" + }, + "[]" + ], + "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-common.ChromeProjectNavigationNode", + "type": "Interface", + "tags": [], + "label": "ChromeProjectNavigationNode", + "description": [], + "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-common.ChromeProjectNavigationNode.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-common.ChromeProjectNavigationNode.link", + "type": "string", + "tags": [], + "label": "link", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-common.ChromeProjectNavigationNode.children", + "type": "Array", + "tags": [], + "label": "children", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-chrome-browser", + "scope": "common", + "docId": "kibKbnCoreChromeBrowserPluginApi", + "section": "def-common.ChromeProjectNavigationNode", + "text": "ChromeProjectNavigationNode" + }, + "[] | undefined" + ], + "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-common.ChromeProjectNavigationNode.title", + "type": "string", + "tags": [], + "label": "title", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-common.ChromeProjectNavigationNode.icon", + "type": "string", + "tags": [], + "label": "icon", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-chrome-browser", "id": "def-common.ChromeRecentlyAccessed", @@ -2315,6 +2441,38 @@ "trackAdoption": false, "children": [], "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-common.ChromeStart.project", + "type": "Object", + "tags": [], + "label": "project", + "description": [ + "\nConfiguration for serverless projects" + ], + "signature": [ + "{ setNavigation(projectNavigation: ", + { + "pluginId": "@kbn/core-chrome-browser", + "scope": "common", + "docId": "kibKbnCoreChromeBrowserPluginApi", + "section": "def-common.ChromeProjectNavigation", + "text": "ChromeProjectNavigation" + }, + "): void; setSideNavComponent(component: ", + { + "pluginId": "@kbn/core-chrome-browser", + "scope": "common", + "docId": "kibKbnCoreChromeBrowserPluginApi", + "section": "def-common.SideNavComponent", + "text": "SideNavComponent" + }, + " | null): void; }" + ], + "path": "packages/core/chrome/core-chrome-browser/src/contracts.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -2370,6 +2528,19 @@ } ], "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-common.SideNavCompProps", + "type": "Interface", + "tags": [], + "label": "SideNavCompProps", + "description": [], + "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "initialIsOpen": false } ], "enums": [], @@ -2453,6 +2624,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-common.ChromeProjectNavigationLink", + "type": "Type", + "tags": [], + "label": "ChromeProjectNavigationLink", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-chrome-browser", "id": "def-common.ChromeStyle", @@ -2467,6 +2653,37 @@ "deprecated": false, "trackAdoption": false, "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-common.SideNavComponent", + "type": "Type", + "tags": [], + "label": "SideNavComponent", + "description": [], + "signature": [ + "React.ComponentClass<", + { + "pluginId": "@kbn/core-chrome-browser", + "scope": "common", + "docId": "kibKbnCoreChromeBrowserPluginApi", + "section": "def-common.SideNavCompProps", + "text": "SideNavCompProps" + }, + ", any> | React.FunctionComponent<", + { + "pluginId": "@kbn/core-chrome-browser", + "scope": "common", + "docId": "kibKbnCoreChromeBrowserPluginApi", + "section": "def-common.SideNavCompProps", + "text": "SideNavCompProps" + }, + ">" + ], + "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false } ], "objects": [] diff --git a/api_docs/kbn_core_chrome_browser.mdx b/api_docs/kbn_core_chrome_browser.mdx index 7472a73f7ea0a..809a5f6924f33 100644 --- a/api_docs/kbn_core_chrome_browser.mdx +++ b/api_docs/kbn_core_chrome_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser title: "@kbn/core-chrome-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser'] --- import kbnCoreChromeBrowserObj from './kbn_core_chrome_browser.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 124 | 0 | 47 | 0 | +| 136 | 0 | 58 | 0 | ## Common diff --git a/api_docs/kbn_core_chrome_browser_mocks.mdx b/api_docs/kbn_core_chrome_browser_mocks.mdx index da9a128259a94..86df711b54588 100644 --- a/api_docs/kbn_core_chrome_browser_mocks.mdx +++ b/api_docs/kbn_core_chrome_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser-mocks title: "@kbn/core-chrome-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 04a2679f79630..608335f0f5e57 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-config-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 d535f42342d2b..39d0a76858821 100644 --- a/api_docs/kbn_core_custom_branding_browser.mdx +++ b/api_docs/kbn_core_custom_branding_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser title: "@kbn/core-custom-branding-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser plugin -date: 2023-05-02 +date: 2023-05-03 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 0b9d37f9f6fc1..ffd4572a49795 100644 --- a/api_docs/kbn_core_custom_branding_browser_internal.mdx +++ b/api_docs/kbn_core_custom_branding_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-internal title: "@kbn/core-custom-branding-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 a1e5b13bd41c5..3584109a10865 100644 --- a/api_docs/kbn_core_custom_branding_browser_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-mocks title: "@kbn/core-custom-branding-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 729e37fba974a..cebb4343c3dc0 100644 --- a/api_docs/kbn_core_custom_branding_common.mdx +++ b/api_docs/kbn_core_custom_branding_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-common title: "@kbn/core-custom-branding-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-common plugin -date: 2023-05-02 +date: 2023-05-03 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 4b0e5c09422e4..42598d9f168ab 100644 --- a/api_docs/kbn_core_custom_branding_server.mdx +++ b/api_docs/kbn_core_custom_branding_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server title: "@kbn/core-custom-branding-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server plugin -date: 2023-05-02 +date: 2023-05-03 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 637a3bac916f4..5d4ffbe1f00e8 100644 --- a/api_docs/kbn_core_custom_branding_server_internal.mdx +++ b/api_docs/kbn_core_custom_branding_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-internal title: "@kbn/core-custom-branding-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 45bfc4c7aaa05..7f0e4a56c38d0 100644 --- a/api_docs/kbn_core_custom_branding_server_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-mocks title: "@kbn/core-custom-branding-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 33567babb319c..7d7b65f776fb9 100644 --- a/api_docs/kbn_core_deprecations_browser.mdx +++ b/api_docs/kbn_core_deprecations_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser title: "@kbn/core-deprecations-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser plugin -date: 2023-05-02 +date: 2023-05-03 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 15acb2eb62661..3d86cc1716b27 100644 --- a/api_docs/kbn_core_deprecations_browser_internal.mdx +++ b/api_docs/kbn_core_deprecations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-internal title: "@kbn/core-deprecations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 f560267c58f84..ef740606d27c3 100644 --- a/api_docs/kbn_core_deprecations_browser_mocks.mdx +++ b/api_docs/kbn_core_deprecations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-mocks title: "@kbn/core-deprecations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 35908dcfe5eb9..4c2387b3b3c1f 100644 --- a/api_docs/kbn_core_deprecations_common.mdx +++ b/api_docs/kbn_core_deprecations_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-common title: "@kbn/core-deprecations-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-common plugin -date: 2023-05-02 +date: 2023-05-03 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 e228f113ae57a..8435c3dbc6038 100644 --- a/api_docs/kbn_core_deprecations_server.mdx +++ b/api_docs/kbn_core_deprecations_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server title: "@kbn/core-deprecations-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server plugin -date: 2023-05-02 +date: 2023-05-03 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 97b093d6e1388..853cef66fa6b1 100644 --- a/api_docs/kbn_core_deprecations_server_internal.mdx +++ b/api_docs/kbn_core_deprecations_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-internal title: "@kbn/core-deprecations-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 2435b233229c5..9a58fbe904a53 100644 --- a/api_docs/kbn_core_deprecations_server_mocks.mdx +++ b/api_docs/kbn_core_deprecations_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-mocks title: "@kbn/core-deprecations-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 886e127d10eac..b6473ecc0926d 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser plugin -date: 2023-05-02 +date: 2023-05-03 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 0b4c62c77cd0a..87311c2aed353 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks title: "@kbn/core-doc-links-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 868ba3998e73e..6d3a5d8cca96c 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server plugin -date: 2023-05-02 +date: 2023-05-03 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 2f05940ad9e02..1f41d77c2d35a 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks title: "@kbn/core-doc-links-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 c29bb96232f7f..7cae7548fd762 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-internal title: "@kbn/core-elasticsearch-client-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 f057952ca3d35..f66ebf1b113d1 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-mocks title: "@kbn/core-elasticsearch-client-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 a577e957464f7..deaf83267883c 100644 --- a/api_docs/kbn_core_elasticsearch_server.mdx +++ b/api_docs/kbn_core_elasticsearch_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server title: "@kbn/core-elasticsearch-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server plugin -date: 2023-05-02 +date: 2023-05-03 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 33f0cc15ca17b..b8b4498fd1b08 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-internal title: "@kbn/core-elasticsearch-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 767023aea9580..b62ba1f41f8a1 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-mocks title: "@kbn/core-elasticsearch-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 c94e6ded0f0a1..0d60f25824585 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 f218e58c8f83c..213c67ac863b8 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 a8e61d903eb03..5a9f5885482f2 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser plugin -date: 2023-05-02 +date: 2023-05-03 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 62d43c326f3e6..14a456cee398b 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-internal title: "@kbn/core-execution-context-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 8122886e42316..eae15652bc19e 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-mocks title: "@kbn/core-execution-context-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 f9fa3e591f395..fca10f572d26e 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-common plugin -date: 2023-05-02 +date: 2023-05-03 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 10741d72bc07d..8583c6b3ab63d 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server plugin -date: 2023-05-02 +date: 2023-05-03 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 f18c19e076a4b..0422be6d3c19b 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-internal title: "@kbn/core-execution-context-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 384a56072e23f..b66f908469f80 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-mocks title: "@kbn/core-execution-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 9ff3858bd4f05..772f3166c0488 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2023-05-02 +date: 2023-05-03 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 2312e407a90f4..4500f167b966b 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser-mocks title: "@kbn/core-fatal-errors-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 e30566d00a675..8eb4e0e9a4922 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser plugin -date: 2023-05-02 +date: 2023-05-03 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 81610bb45c798..9636785ee2059 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 d64c80f00500f..8ca8a5b49f307 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 d98e292a445c0..83cbb13aaaa0b 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-common plugin -date: 2023-05-02 +date: 2023-05-03 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 5a11f091030df..d2eef8a7ce1e7 100644 --- a/api_docs/kbn_core_http_context_server_mocks.mdx +++ b/api_docs/kbn_core_http_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-context-server-mocks title: "@kbn/core-http-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-context-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 d8558ae15b131..15ba9dd7b39f7 100644 --- a/api_docs/kbn_core_http_request_handler_context_server.mdx +++ b/api_docs/kbn_core_http_request_handler_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-request-handler-context-server title: "@kbn/core-http-request-handler-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-request-handler-context-server plugin -date: 2023-05-02 +date: 2023-05-03 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 8398648acd28f..ddc8844d1ac91 100644 --- a/api_docs/kbn_core_http_resources_server.mdx +++ b/api_docs/kbn_core_http_resources_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server title: "@kbn/core-http-resources-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server plugin -date: 2023-05-02 +date: 2023-05-03 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 5dd96bd96da5c..9958f9d7658c4 100644 --- a/api_docs/kbn_core_http_resources_server_internal.mdx +++ b/api_docs/kbn_core_http_resources_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-internal title: "@kbn/core-http-resources-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 63463c9adcd5a..12e3d845dd636 100644 --- a/api_docs/kbn_core_http_resources_server_mocks.mdx +++ b/api_docs/kbn_core_http_resources_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-mocks title: "@kbn/core-http-resources-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 27e4c3bd05e0e..a97c6c78e7431 100644 --- a/api_docs/kbn_core_http_router_server_internal.mdx +++ b/api_docs/kbn_core_http_router_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-internal title: "@kbn/core-http-router-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 49cafd71de34f..c66575db328e3 100644 --- a/api_docs/kbn_core_http_router_server_mocks.mdx +++ b/api_docs/kbn_core_http_router_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-mocks title: "@kbn/core-http-router-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 11e49222e7e34..2fafa63759502 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server plugin -date: 2023-05-02 +date: 2023-05-03 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 da1d230aaf496..d0c31685c24ca 100644 --- a/api_docs/kbn_core_http_server_internal.mdx +++ b/api_docs/kbn_core_http_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-internal title: "@kbn/core-http-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 ecad452284b4f..ef6d72b24da29 100644 --- a/api_docs/kbn_core_http_server_mocks.mdx +++ b/api_docs/kbn_core_http_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-mocks title: "@kbn/core-http-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 8316b94b7207e..568dc159b0a13 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser plugin -date: 2023-05-02 +date: 2023-05-03 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 caa0019b6914c..b009eac1ac877 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 a34d3a8754990..c934cad95a630 100644 --- a/api_docs/kbn_core_i18n_server.mdx +++ b/api_docs/kbn_core_i18n_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server title: "@kbn/core-i18n-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server plugin -date: 2023-05-02 +date: 2023-05-03 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 92b1922776c2a..8b5b6274c39d1 100644 --- a/api_docs/kbn_core_i18n_server_internal.mdx +++ b/api_docs/kbn_core_i18n_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-internal title: "@kbn/core-i18n-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 2df3d1ba2a6ac..7dc4e7833abc1 100644 --- a/api_docs/kbn_core_i18n_server_mocks.mdx +++ b/api_docs/kbn_core_i18n_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-mocks title: "@kbn/core-i18n-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 8d61e75dce5b1..18a66737a5683 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks title: "@kbn/core-injected-metadata-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 dd31322b3e86c..7540e891be9c4 100644 --- a/api_docs/kbn_core_integrations_browser_internal.mdx +++ b/api_docs/kbn_core_integrations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-internal title: "@kbn/core-integrations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 3ecc1b27b4c6c..86ceeda9735ac 100644 --- a/api_docs/kbn_core_integrations_browser_mocks.mdx +++ b/api_docs/kbn_core_integrations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-mocks title: "@kbn/core-integrations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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.devdocs.json b/api_docs/kbn_core_lifecycle_browser.devdocs.json index e2c28606cea12..07098591c9d5a 100644 --- a/api_docs/kbn_core_lifecycle_browser.devdocs.json +++ b/api_docs/kbn_core_lifecycle_browser.devdocs.json @@ -653,6 +653,10 @@ "plugin": "lens", "path": "x-pack/plugins/lens/public/visualizations/xy/visualization.tsx" }, + { + "plugin": "lens", + "path": "x-pack/plugins/lens/public/visualizations/xy/visualization.tsx" + }, { "plugin": "lens", "path": "x-pack/plugins/lens/public/app_plugin/mounter.tsx" diff --git a/api_docs/kbn_core_lifecycle_browser.mdx b/api_docs/kbn_core_lifecycle_browser.mdx index 2751e74b54b6e..61306d31b1ed7 100644 --- a/api_docs/kbn_core_lifecycle_browser.mdx +++ b/api_docs/kbn_core_lifecycle_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser title: "@kbn/core-lifecycle-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser plugin -date: 2023-05-02 +date: 2023-05-03 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 93fdbea426978..1a2a7914281dc 100644 --- a/api_docs/kbn_core_lifecycle_browser_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser-mocks title: "@kbn/core-lifecycle-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 ff72ef7f6958b..357807e562463 100644 --- a/api_docs/kbn_core_lifecycle_server.mdx +++ b/api_docs/kbn_core_lifecycle_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server title: "@kbn/core-lifecycle-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server plugin -date: 2023-05-02 +date: 2023-05-03 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 de10fe69b30a5..113105dddcfe0 100644 --- a/api_docs/kbn_core_lifecycle_server_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server-mocks title: "@kbn/core-lifecycle-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 cc12c176b2e7d..72a726305091e 100644 --- a/api_docs/kbn_core_logging_browser_mocks.mdx +++ b/api_docs/kbn_core_logging_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-browser-mocks title: "@kbn/core-logging-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 ad00164da4ee6..c9ebe9b08d697 100644 --- a/api_docs/kbn_core_logging_common_internal.mdx +++ b/api_docs/kbn_core_logging_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-common-internal title: "@kbn/core-logging-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-common-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 18e77219c17de..14560d31985bd 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server plugin -date: 2023-05-02 +date: 2023-05-03 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 b9f894ae75242..a45c5e2709675 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 c9e60de77d86d..ee9c6a809e4ba 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 036139c1513ff..28175f3909031 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-internal title: "@kbn/core-metrics-collectors-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 9aeb7536f5f52..09ab73526e8b8 100644 --- a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-mocks title: "@kbn/core-metrics-collectors-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 e92e16d7127d9..952265312411f 100644 --- a/api_docs/kbn_core_metrics_server.mdx +++ b/api_docs/kbn_core_metrics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server title: "@kbn/core-metrics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server plugin -date: 2023-05-02 +date: 2023-05-03 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 4136bdc827611..125c2691abf3e 100644 --- a/api_docs/kbn_core_metrics_server_internal.mdx +++ b/api_docs/kbn_core_metrics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-internal title: "@kbn/core-metrics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 96d027f076821..83d8c888d107d 100644 --- a/api_docs/kbn_core_metrics_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-mocks title: "@kbn/core-metrics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 e685246827364..e13cc5b122815 100644 --- a/api_docs/kbn_core_mount_utils_browser.mdx +++ b/api_docs/kbn_core_mount_utils_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser title: "@kbn/core-mount-utils-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-mount-utils-browser plugin -date: 2023-05-02 +date: 2023-05-03 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 af2e3789e2f5c..cae0cafe0ad8d 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server plugin -date: 2023-05-02 +date: 2023-05-03 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 b452eb9eb41b7..c2842c8b495cc 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 1d76937035cad..43439adf96ba0 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 1ac24ec432c79..512cf7b29ea17 100644 --- a/api_docs/kbn_core_notifications_browser.mdx +++ b/api_docs/kbn_core_notifications_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser title: "@kbn/core-notifications-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser plugin -date: 2023-05-02 +date: 2023-05-03 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 49cd5859b77d8..227ad5921c69c 100644 --- a/api_docs/kbn_core_notifications_browser_internal.mdx +++ b/api_docs/kbn_core_notifications_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-internal title: "@kbn/core-notifications-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 a6c2d9ddd7223..1e40f2aa0c1e3 100644 --- a/api_docs/kbn_core_notifications_browser_mocks.mdx +++ b/api_docs/kbn_core_notifications_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-mocks title: "@kbn/core-notifications-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 ab8c6e7254e94..2fea973911b7f 100644 --- a/api_docs/kbn_core_overlays_browser.mdx +++ b/api_docs/kbn_core_overlays_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser title: "@kbn/core-overlays-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser plugin -date: 2023-05-02 +date: 2023-05-03 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 aa0c36ee3b8af..e27df222f8b00 100644 --- a/api_docs/kbn_core_overlays_browser_internal.mdx +++ b/api_docs/kbn_core_overlays_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-internal title: "@kbn/core-overlays-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 216828288424d..7234bec870445 100644 --- a/api_docs/kbn_core_overlays_browser_mocks.mdx +++ b/api_docs/kbn_core_overlays_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-mocks title: "@kbn/core-overlays-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 76e81086bdb55..01d41095411e8 100644 --- a/api_docs/kbn_core_plugins_browser.mdx +++ b/api_docs/kbn_core_plugins_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser title: "@kbn/core-plugins-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser plugin -date: 2023-05-02 +date: 2023-05-03 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 39abaa69e52f1..ffe33cd248aca 100644 --- a/api_docs/kbn_core_plugins_browser_mocks.mdx +++ b/api_docs/kbn_core_plugins_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser-mocks title: "@kbn/core-plugins-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser-mocks'] --- import kbnCorePluginsBrowserMocksObj from './kbn_core_plugins_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server.mdx b/api_docs/kbn_core_plugins_server.mdx index 1006dbec9176d..97d91ccc21aa5 100644 --- a/api_docs/kbn_core_plugins_server.mdx +++ b/api_docs/kbn_core_plugins_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server title: "@kbn/core-plugins-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server plugin -date: 2023-05-02 +date: 2023-05-03 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 fcd9830c526e8..d225043a9292a 100644 --- a/api_docs/kbn_core_plugins_server_mocks.mdx +++ b/api_docs/kbn_core_plugins_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server-mocks title: "@kbn/core-plugins-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 b5015710fa9d5..d95c12d28e6a7 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server plugin -date: 2023-05-02 +date: 2023-05-03 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 7fabd007f3da6..12227d7eefc82 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 0a911a89313de..4553ec2a91542 100644 --- a/api_docs/kbn_core_rendering_browser_mocks.mdx +++ b/api_docs/kbn_core_rendering_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-browser-mocks title: "@kbn/core-rendering-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 f142a520ebc56..f43f63bc96310 100644 --- a/api_docs/kbn_core_rendering_server_internal.mdx +++ b/api_docs/kbn_core_rendering_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-internal title: "@kbn/core-rendering-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 87c5d69b88dee..4c1ee03bb616d 100644 --- a/api_docs/kbn_core_rendering_server_mocks.mdx +++ b/api_docs/kbn_core_rendering_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-mocks title: "@kbn/core-rendering-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 9ae16cbf41e8f..0944dd58b1a0b 100644 --- a/api_docs/kbn_core_root_server_internal.mdx +++ b/api_docs/kbn_core_root_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-root-server-internal title: "@kbn/core-root-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-root-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 2c631713f5db4..326b24876205e 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.mdx +++ b/api_docs/kbn_core_saved_objects_api_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-browser title: "@kbn/core-saved-objects-api-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-browser plugin -date: 2023-05-02 +date: 2023-05-03 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 d84981d941804..cb39e3460f0b3 100644 --- a/api_docs/kbn_core_saved_objects_api_server.mdx +++ b/api_docs/kbn_core_saved_objects_api_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server title: "@kbn/core-saved-objects-api-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server'] --- import kbnCoreSavedObjectsApiServerObj from './kbn_core_saved_objects_api_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server_internal.mdx b/api_docs/kbn_core_saved_objects_api_server_internal.mdx index 088c91e5c646c..c3c1ae1009cd0 100644 --- a/api_docs/kbn_core_saved_objects_api_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-internal title: "@kbn/core-saved-objects-api-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-internal'] --- import kbnCoreSavedObjectsApiServerInternalObj from './kbn_core_saved_objects_api_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx index d791b8e7a164f..c95317a37a91f 100644 --- a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-mocks title: "@kbn/core-saved-objects-api-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-mocks'] --- import kbnCoreSavedObjectsApiServerMocksObj from './kbn_core_saved_objects_api_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_internal.devdocs.json b/api_docs/kbn_core_saved_objects_base_server_internal.devdocs.json index 2ed87324189c4..ee1fc2c531871 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.devdocs.json +++ b/api_docs/kbn_core_saved_objects_base_server_internal.devdocs.json @@ -189,10 +189,10 @@ }, { "parentPluginId": "@kbn/core-saved-objects-base-server-internal", - "id": "def-common.compareModelVersions", + "id": "def-common.compareVirtualVersions", "type": "Function", "tags": [], - "label": "compareModelVersions", + "label": "compareVirtualVersions", "description": [], "signature": [ "({ appVersions, indexVersions, deletedTypes, }: ", @@ -218,7 +218,7 @@ "children": [ { "parentPluginId": "@kbn/core-saved-objects-base-server-internal", - "id": "def-common.compareModelVersions.$1", + "id": "def-common.compareVirtualVersions.$1", "type": "Object", "tags": [], "label": "{\n appVersions,\n indexVersions,\n deletedTypes,\n}", @@ -422,6 +422,56 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getCurrentVirtualVersion", + "type": "Function", + "tags": [], + "label": "getCurrentVirtualVersion", + "description": [ + "\nReturns the current virtual version for the given type.\nIt will either be the latest model version if the type\nalready switched to using them (switchToModelVersionAt is set),\nor the latest migration version for the type otherwise." + ], + "signature": [ + "(type: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectsType", + "text": "SavedObjectsType" + }, + ") => string" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getCurrentVirtualVersion.$1", + "type": "Object", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectsType", + "text": "SavedObjectsType" + }, + "" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-saved-objects-base-server-internal", "id": "def-common.getIndexForType", @@ -455,6 +505,54 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getLatestMigrationVersion", + "type": "Function", + "tags": [], + "label": "getLatestMigrationVersion", + "description": [], + "signature": [ + "(type: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectsType", + "text": "SavedObjectsType" + }, + ") => string" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getLatestMigrationVersion.$1", + "type": "Object", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectsType", + "text": "SavedObjectsType" + }, + "" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-saved-objects-base-server-internal", "id": "def-common.getLatestModelVersion", @@ -597,96 +695,6 @@ "returnComment": [], "initialIsOpen": false }, - { - "parentPluginId": "@kbn/core-saved-objects-base-server-internal", - "id": "def-common.getModelVersionsFromMappingMeta", - "type": "Function", - "tags": [], - "label": "getModelVersionsFromMappingMeta", - "description": [ - "\nBuild the version map from the specified source of the provided mappings meta." - ], - "signature": [ - "({ meta, source, knownTypes, }: ", - "GetModelVersionsFromMappingMetaOpts", - ") => ", - { - "pluginId": "@kbn/core-saved-objects-base-server-internal", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsBaseServerInternalPluginApi", - "section": "def-common.ModelVersionMap", - "text": "ModelVersionMap" - }, - " | undefined" - ], - "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-base-server-internal", - "id": "def-common.getModelVersionsFromMappingMeta.$1", - "type": "Object", - "tags": [], - "label": "{\n meta,\n source,\n knownTypes,\n}", - "description": [], - "signature": [ - "GetModelVersionsFromMappingMetaOpts" - ], - "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/core-saved-objects-base-server-internal", - "id": "def-common.getModelVersionsFromMappings", - "type": "Function", - "tags": [], - "label": "getModelVersionsFromMappings", - "description": [ - "\nBuild the version map from the specified source of the provided mappings." - ], - "signature": [ - "({ mappings, source, knownTypes, }: ", - "GetModelVersionsFromMappingsOpts", - ") => ", - { - "pluginId": "@kbn/core-saved-objects-base-server-internal", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsBaseServerInternalPluginApi", - "section": "def-common.ModelVersionMap", - "text": "ModelVersionMap" - }, - " | undefined" - ], - "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/core-saved-objects-base-server-internal", - "id": "def-common.getModelVersionsFromMappings.$1", - "type": "Object", - "tags": [], - "label": "{\n mappings,\n source,\n knownTypes,\n}", - "description": [], - "signature": [ - "GetModelVersionsFromMappingsOpts" - ], - "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, { "parentPluginId": "@kbn/core-saved-objects-base-server-internal", "id": "def-common.getProperty", @@ -884,6 +892,153 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getVirtualVersionMap", + "type": "Function", + "tags": [], + "label": "getVirtualVersionMap", + "description": [ + "\nReturns a map of virtual model version for the given types.\nSee {@link getCurrentVirtualVersion}" + ], + "signature": [ + "(types: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectsType", + "text": "SavedObjectsType" + }, + "[]) => ", + { + "pluginId": "@kbn/core-saved-objects-base-server-internal", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsBaseServerInternalPluginApi", + "section": "def-common.VirtualVersionMap", + "text": "VirtualVersionMap" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getVirtualVersionMap.$1", + "type": "Array", + "tags": [], + "label": "types", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectsType", + "text": "SavedObjectsType" + }, + "[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getVirtualVersionsFromMappingMeta", + "type": "Function", + "tags": [], + "label": "getVirtualVersionsFromMappingMeta", + "description": [ + "\nBuild the version map from the specified source of the provided mappings meta." + ], + "signature": [ + "({ meta, source, knownTypes, }: ", + "GetModelVersionsFromMappingMetaOpts", + ") => ", + { + "pluginId": "@kbn/core-saved-objects-base-server-internal", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsBaseServerInternalPluginApi", + "section": "def-common.VirtualVersionMap", + "text": "VirtualVersionMap" + }, + " | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getVirtualVersionsFromMappingMeta.$1", + "type": "Object", + "tags": [], + "label": "{\n meta,\n source,\n knownTypes,\n}", + "description": [], + "signature": [ + "GetModelVersionsFromMappingMetaOpts" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getVirtualVersionsFromMappings", + "type": "Function", + "tags": [], + "label": "getVirtualVersionsFromMappings", + "description": [ + "\nBuild the version map from the specified source of the provided mappings." + ], + "signature": [ + "({ mappings, source, knownTypes, }: ", + "GetModelVersionsFromMappingsOpts", + ") => ", + { + "pluginId": "@kbn/core-saved-objects-base-server-internal", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsBaseServerInternalPluginApi", + "section": "def-common.VirtualVersionMap", + "text": "VirtualVersionMap" + }, + " | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.getVirtualVersionsFromMappings.$1", + "type": "Object", + "tags": [], + "label": "{\n mappings,\n source,\n knownTypes,\n}", + "description": [], + "signature": [ + "GetModelVersionsFromMappingsOpts" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-saved-objects-base-server-internal", "id": "def-common.isVirtualModelVersion", @@ -1068,7 +1223,7 @@ "The latest model version of the types registered in the application" ], "signature": [ - "{ [x: string]: number; }" + "{ [x: string]: string; }" ], "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.ts", "deprecated": false, @@ -1084,7 +1239,7 @@ "The model version stored in the index" ], "signature": [ - "{ [x: string]: number; }" + "{ [x: string]: string; }" ], "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.ts", "deprecated": false, @@ -1183,7 +1338,9 @@ "type": "Type", "tags": [], "label": "ModelVersionMap", - "description": [], + "description": [ + "\nA map of SO type name to Model Version." + ], "signature": [ "{ [x: string]: number; }" ], @@ -1254,6 +1411,23 @@ "deprecated": false, "trackAdoption": false, "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-common.VirtualVersionMap", + "type": "Type", + "tags": [], + "label": "VirtualVersionMap", + "description": [ + "\nA map of SO type name to {@link VirtualVersion}." + ], + "signature": [ + "{ [x: string]: string; }" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false } ], "objects": [ 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 7345281ea6e21..d5fa0a63ffe32 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-internal title: "@kbn/core-saved-objects-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-internal'] --- import kbnCoreSavedObjectsBaseServerInternalObj from './kbn_core_saved_objects_base_server_internal.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 72 | 0 | 52 | 9 | +| 79 | 0 | 55 | 9 | ## Common 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 bdf62d1555291..ac1325b1c0578 100644 --- a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-mocks title: "@kbn/core-saved-objects-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 45a8cf14cd592..08a7f7f001f26 100644 --- a/api_docs/kbn_core_saved_objects_browser.mdx +++ b/api_docs/kbn_core_saved_objects_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser title: "@kbn/core-saved-objects-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser plugin -date: 2023-05-02 +date: 2023-05-03 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 73978ebafce7b..588cccbbbd4a6 100644 --- a/api_docs/kbn_core_saved_objects_browser_internal.mdx +++ b/api_docs/kbn_core_saved_objects_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-internal title: "@kbn/core-saved-objects-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 e29e2d024b223..482e6ce5f0274 100644 --- a/api_docs/kbn_core_saved_objects_browser_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-mocks title: "@kbn/core-saved-objects-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 d50c0ddffa7df..2e55ce5ce25c0 100644 --- a/api_docs/kbn_core_saved_objects_common.mdx +++ b/api_docs/kbn_core_saved_objects_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-common title: "@kbn/core-saved-objects-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-common plugin -date: 2023-05-02 +date: 2023-05-03 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 e0dc58f76c2d4..1395fd377174c 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-internal title: "@kbn/core-saved-objects-import-export-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 dc13873aaf248..072a802c2884b 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-mocks title: "@kbn/core-saved-objects-import-export-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 f62ece5e769f7..77f7224395b6d 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-internal title: "@kbn/core-saved-objects-migration-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 f5d3ca4298ac7..f9837f90d7b35 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-mocks title: "@kbn/core-saved-objects-migration-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 aef6f7d4c132c..e7ad364d43c92 100644 --- a/api_docs/kbn_core_saved_objects_server.mdx +++ b/api_docs/kbn_core_saved_objects_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server title: "@kbn/core-saved-objects-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server plugin -date: 2023-05-02 +date: 2023-05-03 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 34ee8e4c1f181..2d94a1423e1f4 100644 --- a/api_docs/kbn_core_saved_objects_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-internal title: "@kbn/core-saved-objects-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 b5c503c86ad9a..9f90c262c567d 100644 --- a/api_docs/kbn_core_saved_objects_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-mocks title: "@kbn/core-saved-objects-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 e2a46627b58b3..7cabdc9d3e20b 100644 --- a/api_docs/kbn_core_saved_objects_utils_server.mdx +++ b/api_docs/kbn_core_saved_objects_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-utils-server title: "@kbn/core-saved-objects-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-utils-server plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-utils-server'] --- import kbnCoreSavedObjectsUtilsServerObj from './kbn_core_saved_objects_utils_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx index e13c289ef986e..d69103579b021 100644 --- a/api_docs/kbn_core_status_common.mdx +++ b/api_docs/kbn_core_status_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common title: "@kbn/core-status-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common plugin -date: 2023-05-02 +date: 2023-05-03 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 b9b4db5b37dc1..262d30ebce8e0 100644 --- a/api_docs/kbn_core_status_common_internal.mdx +++ b/api_docs/kbn_core_status_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common-internal title: "@kbn/core-status-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 b999919ce2d55..fa2c663855de9 100644 --- a/api_docs/kbn_core_status_server.mdx +++ b/api_docs/kbn_core_status_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server title: "@kbn/core-status-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server plugin -date: 2023-05-02 +date: 2023-05-03 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 3efd3cb8d8bb4..fbf346ff4f4af 100644 --- a/api_docs/kbn_core_status_server_internal.mdx +++ b/api_docs/kbn_core_status_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-internal title: "@kbn/core-status-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 7dcc2f2be49aa..4ed1193d9fc88 100644 --- a/api_docs/kbn_core_status_server_mocks.mdx +++ b/api_docs/kbn_core_status_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-mocks title: "@kbn/core-status-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 d1768efef0bd8..7ca3a629205ab 100644 --- a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-deprecations-getters title: "@kbn/core-test-helpers-deprecations-getters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-deprecations-getters plugin -date: 2023-05-02 +date: 2023-05-03 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 ad1d063b438bc..c1e731ddba06a 100644 --- a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx +++ b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-http-setup-browser title: "@kbn/core-test-helpers-http-setup-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-http-setup-browser plugin -date: 2023-05-02 +date: 2023-05-03 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 e1ec220a17c78..09c044e82c057 100644 --- a/api_docs/kbn_core_test_helpers_kbn_server.mdx +++ b/api_docs/kbn_core_test_helpers_kbn_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-kbn-server title: "@kbn/core-test-helpers-kbn-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-kbn-server plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-kbn-server'] --- import kbnCoreTestHelpersKbnServerObj from './kbn_core_test_helpers_kbn_server.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx index f2387bc8d18d3..ebf78d0f6071c 100644 --- a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx +++ b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-so-type-serializer title: "@kbn/core-test-helpers-so-type-serializer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-so-type-serializer plugin -date: 2023-05-02 +date: 2023-05-03 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 c08ecee73504d..862f26f836030 100644 --- a/api_docs/kbn_core_test_helpers_test_utils.mdx +++ b/api_docs/kbn_core_test_helpers_test_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-test-utils title: "@kbn/core-test-helpers-test-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-test-utils plugin -date: 2023-05-02 +date: 2023-05-03 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 f8f81a1e7a3d2..8110a4a0613d9 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser'] --- import kbnCoreThemeBrowserObj from './kbn_core_theme_browser.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_internal.mdx b/api_docs/kbn_core_theme_browser_internal.mdx index 0edede0889f2d..d8619f36fcabc 100644 --- a/api_docs/kbn_core_theme_browser_internal.mdx +++ b/api_docs/kbn_core_theme_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-internal title: "@kbn/core-theme-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-internal plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-internal'] --- import kbnCoreThemeBrowserInternalObj from './kbn_core_theme_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index f7e868df0b2bd..a9ef485e5387d 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 1c94693951ddd..0283a2719cbd8 100644 --- a/api_docs/kbn_core_ui_settings_browser.mdx +++ b/api_docs/kbn_core_ui_settings_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser title: "@kbn/core-ui-settings-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser plugin -date: 2023-05-02 +date: 2023-05-03 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 f672b61c7bfc1..77a874b0c8b1e 100644 --- a/api_docs/kbn_core_ui_settings_browser_internal.mdx +++ b/api_docs/kbn_core_ui_settings_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-internal title: "@kbn/core-ui-settings-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 c4a11bfb6313b..8ef4d9f97f4f0 100644 --- a/api_docs/kbn_core_ui_settings_browser_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-mocks title: "@kbn/core-ui-settings-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 d5a0abe448422..aabe3197c8c66 100644 --- a/api_docs/kbn_core_ui_settings_common.mdx +++ b/api_docs/kbn_core_ui_settings_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-common title: "@kbn/core-ui-settings-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-common plugin -date: 2023-05-02 +date: 2023-05-03 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 240a714088a44..bd9bdb11d8030 100644 --- a/api_docs/kbn_core_ui_settings_server.mdx +++ b/api_docs/kbn_core_ui_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server title: "@kbn/core-ui-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server plugin -date: 2023-05-02 +date: 2023-05-03 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 a5e463c12af22..81fd17e76d2b5 100644 --- a/api_docs/kbn_core_ui_settings_server_internal.mdx +++ b/api_docs/kbn_core_ui_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-internal title: "@kbn/core-ui-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 bd7ddfe045824..2236c02c290b2 100644 --- a/api_docs/kbn_core_ui_settings_server_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-mocks title: "@kbn/core-ui-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 be0db1a3c3e11..f3ec952e372e5 100644 --- a/api_docs/kbn_core_usage_data_server.mdx +++ b/api_docs/kbn_core_usage_data_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server title: "@kbn/core-usage-data-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server plugin -date: 2023-05-02 +date: 2023-05-03 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 824a196b1fe47..dae96d610437c 100644 --- a/api_docs/kbn_core_usage_data_server_internal.mdx +++ b/api_docs/kbn_core_usage_data_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-internal title: "@kbn/core-usage-data-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-internal plugin -date: 2023-05-02 +date: 2023-05-03 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 26335c291fe72..de2e800bb2895 100644 --- a/api_docs/kbn_core_usage_data_server_mocks.mdx +++ b/api_docs/kbn_core_usage_data_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-mocks title: "@kbn/core-usage-data-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 495d66e7fe762..ebfd18db3f524 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: 2023-05-02 +date: 2023-05-03 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 cbae4e943d867..2b13d3142c24e 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: 2023-05-02 +date: 2023-05-03 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 b9a1ddb7c0012..d62ee56946fc7 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: 2023-05-02 +date: 2023-05-03 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 2cdda5a01c9b6..8fe67a6c2beca 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto plugin -date: 2023-05-02 +date: 2023-05-03 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 ae0ed28599a62..dc4a139fe94fb 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto-browser plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_cypress_config.mdx b/api_docs/kbn_cypress_config.mdx index 840c70b43079d..7f47901750fc4 100644 --- a/api_docs/kbn_cypress_config.mdx +++ b/api_docs/kbn_cypress_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cypress-config title: "@kbn/cypress-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cypress-config plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cypress-config'] --- import kbnCypressConfigObj from './kbn_cypress_config.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index 18b927eeb6977..83f4cd178be3e 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/datemath plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index dcc65c0c9c29a..da8d00957ce6b 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-errors plugin -date: 2023-05-02 +date: 2023-05-03 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 7dc6f00a6a207..f1e91918c394c 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-runner plugin -date: 2023-05-02 +date: 2023-05-03 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 27b2d51421ecf..09201cfe221b4 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-proc-runner plugin -date: 2023-05-02 +date: 2023-05-03 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 eb894c300e26d..8d668d174eeef 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-utils plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index b57afb3788246..ebde20f97c11e 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/doc-links plugin -date: 2023-05-02 +date: 2023-05-03 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 6293354646526..b156bddfb1fad 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/docs-utils plugin -date: 2023-05-02 +date: 2023-05-03 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 134985285ce73..1890f724661a3 100644 --- a/api_docs/kbn_dom_drag_drop.mdx +++ b/api_docs/kbn_dom_drag_drop.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dom-drag-drop title: "@kbn/dom-drag-drop" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dom-drag-drop plugin -date: 2023-05-02 +date: 2023-05-03 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 f3f390fca15d4..3b8acf46301ba 100644 --- a/api_docs/kbn_ebt_tools.mdx +++ b/api_docs/kbn_ebt_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ebt-tools title: "@kbn/ebt-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ebt-tools plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_ecs.mdx b/api_docs/kbn_ecs.mdx index 8ab3b55d4b737..8029cf7afce16 100644 --- a/api_docs/kbn_ecs.mdx +++ b/api_docs/kbn_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs title: "@kbn/ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs'] --- import kbnEcsObj from './kbn_ecs.devdocs.json'; diff --git a/api_docs/kbn_ecs_data_quality_dashboard.mdx b/api_docs/kbn_ecs_data_quality_dashboard.mdx index 01002a48ee18c..41f16366edbc0 100644 --- a/api_docs/kbn_ecs_data_quality_dashboard.mdx +++ b/api_docs/kbn_ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs-data-quality-dashboard title: "@kbn/ecs-data-quality-dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs-data-quality-dashboard plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs-data-quality-dashboard'] --- import kbnEcsDataQualityDashboardObj from './kbn_ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index 714a2382f3071..ff89b967b7d71 100644 --- a/api_docs/kbn_es.mdx +++ b/api_docs/kbn_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es title: "@kbn/es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es plugin -date: 2023-05-02 +date: 2023-05-03 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 c577314934e79..fb738c1d325bd 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-archiver plugin -date: 2023-05-02 +date: 2023-05-03 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 968d7fd59eab7..cc10622a7a1ad 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-errors plugin -date: 2023-05-02 +date: 2023-05-03 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 9da712028b66d..89c02f9d827e6 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-query plugin -date: 2023-05-02 +date: 2023-05-03 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 6a3235389901a..1d46f44755dfd 100644 --- a/api_docs/kbn_es_types.mdx +++ b/api_docs/kbn_es_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-types title: "@kbn/es-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-types plugin -date: 2023-05-02 +date: 2023-05-03 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 898eca964a81e..46b63f6a3ad6e 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/eslint-plugin-imports plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_expandable_flyout.mdx b/api_docs/kbn_expandable_flyout.mdx index 1ac253792b595..1afa7c818eb47 100644 --- a/api_docs/kbn_expandable_flyout.mdx +++ b/api_docs/kbn_expandable_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-expandable-flyout title: "@kbn/expandable-flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/expandable-flyout plugin -date: 2023-05-02 +date: 2023-05-03 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 97cda45671b41..2e659e77001b7 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-types plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index 18cdbb13663ec..3a7d3d12547ae 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/find-used-node-modules plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] --- import kbnFindUsedNodeModulesObj from './kbn_find_used_node_modules.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index 56b7aeed37764..f54cb36ed6393 100644 --- a/api_docs/kbn_ftr_common_functional_services.mdx +++ b/api_docs/kbn_ftr_common_functional_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-services title: "@kbn/ftr-common-functional-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-services plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-services'] --- import kbnFtrCommonFunctionalServicesObj from './kbn_ftr_common_functional_services.devdocs.json'; diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index c9db7c874573b..b8c70b8c2809f 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_generate_csv.mdx b/api_docs/kbn_generate_csv.mdx index 75242833a2444..e74750969e776 100644 --- a/api_docs/kbn_generate_csv.mdx +++ b/api_docs/kbn_generate_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv title: "@kbn/generate-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv'] --- import kbnGenerateCsvObj from './kbn_generate_csv.devdocs.json'; diff --git a/api_docs/kbn_generate_csv_types.mdx b/api_docs/kbn_generate_csv_types.mdx index 1e5b4e490273e..d3a4173d07201 100644 --- a/api_docs/kbn_generate_csv_types.mdx +++ b/api_docs/kbn_generate_csv_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv-types title: "@kbn/generate-csv-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv-types plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv-types'] --- import kbnGenerateCsvTypesObj from './kbn_generate_csv_types.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index 1fcb39d48b72e..bdc1d8287d850 100644 --- a/api_docs/kbn_guided_onboarding.mdx +++ b/api_docs/kbn_guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-guided-onboarding title: "@kbn/guided-onboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/guided-onboarding plugin -date: 2023-05-02 +date: 2023-05-03 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 51bfd4b366271..f57f175ed0afd 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/handlebars plugin -date: 2023-05-02 +date: 2023-05-03 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 5891a9b0c1e7c..f70c8e46066bc 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/hapi-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 243dce49546c1..52a186e26decc 100644 --- a/api_docs/kbn_health_gateway_server.mdx +++ b/api_docs/kbn_health_gateway_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-health-gateway-server title: "@kbn/health-gateway-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/health-gateway-server plugin -date: 2023-05-02 +date: 2023-05-03 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 3f00a9300210f..690880b9ec667 100644 --- a/api_docs/kbn_home_sample_data_card.mdx +++ b/api_docs/kbn_home_sample_data_card.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-card title: "@kbn/home-sample-data-card" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-card plugin -date: 2023-05-02 +date: 2023-05-03 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 bd688c531b0fd..5ee8d6f1927c6 100644 --- a/api_docs/kbn_home_sample_data_tab.mdx +++ b/api_docs/kbn_home_sample_data_tab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-tab title: "@kbn/home-sample-data-tab" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-tab plugin -date: 2023-05-02 +date: 2023-05-03 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 63d4b5b33080d..5d24a2ab60ea8 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n plugin -date: 2023-05-02 +date: 2023-05-03 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 0bd09da52f6a9..9bc204e9bfcd0 100644 --- a/api_docs/kbn_i18n_react.mdx +++ b/api_docs/kbn_i18n_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n-react title: "@kbn/i18n-react" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n-react plugin -date: 2023-05-02 +date: 2023-05-03 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 5db68f949ac84..1d68d62adc756 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/import-resolver plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index 636e766957f0c..8a2aa19a4a850 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/interpreter plugin -date: 2023-05-02 +date: 2023-05-03 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 1a255b9d0195c..2011412663cf4 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/io-ts-utils plugin -date: 2023-05-02 +date: 2023-05-03 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 09895705c841b..614c09f2ae7c7 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/jest-serializers plugin -date: 2023-05-02 +date: 2023-05-03 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 b1c38092f2c5f..cf1fd9220d5c4 100644 --- a/api_docs/kbn_journeys.mdx +++ b/api_docs/kbn_journeys.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-journeys title: "@kbn/journeys" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/journeys plugin -date: 2023-05-02 +date: 2023-05-03 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 99d9d4d83d092..663904b9c24a9 100644 --- a/api_docs/kbn_json_ast.mdx +++ b/api_docs/kbn_json_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-ast title: "@kbn/json-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-ast plugin -date: 2023-05-02 +date: 2023-05-03 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 c0b4286d220ed..6537f8da23e78 100644 --- a/api_docs/kbn_kibana_manifest_schema.mdx +++ b/api_docs/kbn_kibana_manifest_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-schema title: "@kbn/kibana-manifest-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-schema plugin -date: 2023-05-02 +date: 2023-05-03 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 962ba6905f63c..732ee400e1d11 100644 --- a/api_docs/kbn_language_documentation_popover.mdx +++ b/api_docs/kbn_language_documentation_popover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-language-documentation-popover title: "@kbn/language-documentation-popover" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/language-documentation-popover plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/language-documentation-popover'] --- import kbnLanguageDocumentationPopoverObj from './kbn_language_documentation_popover.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index 33f9c2f06736d..fa2b913b56291 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging plugin -date: 2023-05-02 +date: 2023-05-03 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 21a992ce2832d..3905dd2babfd4 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging-mocks plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index 3542b2901ad2a..c22eaf624e6f8 100644 --- a/api_docs/kbn_managed_vscode_config.mdx +++ b/api_docs/kbn_managed_vscode_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-vscode-config title: "@kbn/managed-vscode-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-vscode-config plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index 1be34e57cf2e2..240428161d787 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mapbox-gl plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index d19be692451ed..c355df11ed99e 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-agg-utils plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-agg-utils'] --- import kbnMlAggUtilsObj from './kbn_ml_agg_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_date_picker.mdx b/api_docs/kbn_ml_date_picker.mdx index 0f609675648ee..81191e9d2823f 100644 --- a/api_docs/kbn_ml_date_picker.mdx +++ b/api_docs/kbn_ml_date_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-picker title: "@kbn/ml-date-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-picker plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-picker'] --- import kbnMlDatePickerObj from './kbn_ml_date_picker.devdocs.json'; diff --git a/api_docs/kbn_ml_error_utils.mdx b/api_docs/kbn_ml_error_utils.mdx index 33d509caee595..30257a29a8ef4 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: 2023-05-02 +date: 2023-05-03 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_is_defined.mdx b/api_docs/kbn_ml_is_defined.mdx index 8e6fe64b66d16..0156a9bf0af56 100644 --- a/api_docs/kbn_ml_is_defined.mdx +++ b/api_docs/kbn_ml_is_defined.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-defined title: "@kbn/ml-is-defined" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-defined plugin -date: 2023-05-02 +date: 2023-05-03 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 3881cff29c084..64adbf9b244eb 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-populated-object plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-populated-object'] --- import kbnMlIsPopulatedObjectObj from './kbn_ml_is_populated_object.devdocs.json'; diff --git a/api_docs/kbn_ml_local_storage.mdx b/api_docs/kbn_ml_local_storage.mdx index b6cb60042388a..129b6a3c8a23d 100644 --- a/api_docs/kbn_ml_local_storage.mdx +++ b/api_docs/kbn_ml_local_storage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-local-storage title: "@kbn/ml-local-storage" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-local-storage plugin -date: 2023-05-02 +date: 2023-05-03 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 e9f637ac5656c..6ea2934256060 100644 --- a/api_docs/kbn_ml_nested_property.mdx +++ b/api_docs/kbn_ml_nested_property.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-nested-property title: "@kbn/ml-nested-property" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-nested-property plugin -date: 2023-05-02 +date: 2023-05-03 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 486f4cca60ed4..cd7d3acc80dd9 100644 --- a/api_docs/kbn_ml_number_utils.mdx +++ b/api_docs/kbn_ml_number_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-number-utils title: "@kbn/ml-number-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-number-utils plugin -date: 2023-05-02 +date: 2023-05-03 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 f9fbf64e9e98a..1b2411a124509 100644 --- a/api_docs/kbn_ml_query_utils.mdx +++ b/api_docs/kbn_ml_query_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-query-utils title: "@kbn/ml-query-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-query-utils plugin -date: 2023-05-02 +date: 2023-05-03 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 45359f38e1eba..cbae41c4a777f 100644 --- a/api_docs/kbn_ml_random_sampler_utils.mdx +++ b/api_docs/kbn_ml_random_sampler_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-random-sampler-utils title: "@kbn/ml-random-sampler-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-random-sampler-utils plugin -date: 2023-05-02 +date: 2023-05-03 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 94d11e536f2ad..4d3812c7b5f70 100644 --- a/api_docs/kbn_ml_route_utils.mdx +++ b/api_docs/kbn_ml_route_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-route-utils title: "@kbn/ml-route-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-route-utils plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-route-utils'] --- import kbnMlRouteUtilsObj from './kbn_ml_route_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index 8bd318a808f24..9615e65aaff0f 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-string-hash plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_ml_trained_models_utils.mdx b/api_docs/kbn_ml_trained_models_utils.mdx index 4289fed179bcc..0d21a9307b57a 100644 --- a/api_docs/kbn_ml_trained_models_utils.mdx +++ b/api_docs/kbn_ml_trained_models_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-trained-models-utils title: "@kbn/ml-trained-models-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-trained-models-utils plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-trained-models-utils'] --- import kbnMlTrainedModelsUtilsObj from './kbn_ml_trained_models_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_url_state.mdx b/api_docs/kbn_ml_url_state.mdx index e0e1dba909c90..c2d9937e61fb9 100644 --- a/api_docs/kbn_ml_url_state.mdx +++ b/api_docs/kbn_ml_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-url-state title: "@kbn/ml-url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-url-state plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index d900175154f15..b08864e93d66a 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/monaco plugin -date: 2023-05-02 +date: 2023-05-03 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 3b0f7f8135892..ddc24ba134229 100644 --- a/api_docs/kbn_object_versioning.mdx +++ b/api_docs/kbn_object_versioning.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-object-versioning title: "@kbn/object-versioning" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/object-versioning plugin -date: 2023-05-02 +date: 2023-05-03 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 65871a423fdb5..26e89e1e93b11 100644 --- a/api_docs/kbn_observability_alert_details.mdx +++ b/api_docs/kbn_observability_alert_details.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alert-details title: "@kbn/observability-alert-details" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alert-details plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alert-details'] --- import kbnObservabilityAlertDetailsObj from './kbn_observability_alert_details.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index a6a14a3ad0911..6b6e9a73f41d7 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer plugin -date: 2023-05-02 +date: 2023-05-03 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 f7364261ac87f..cc6f384034e1b 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2023-05-02 +date: 2023-05-03 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 85d79608d3763..1a579c49bd1be 100644 --- a/api_docs/kbn_osquery_io_ts_types.mdx +++ b/api_docs/kbn_osquery_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-osquery-io-ts-types title: "@kbn/osquery-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/osquery-io-ts-types plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/osquery-io-ts-types'] --- import kbnOsqueryIoTsTypesObj from './kbn_osquery_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index 84ba3406c0d6f..e989f06b612dc 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/performance-testing-dataset-extractor'] --- import kbnPerformanceTestingDatasetExtractorObj from './kbn_performance_testing_dataset_extractor.devdocs.json'; diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index 5b57ab30295fa..f81d7cbf6f105 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-generator plugin -date: 2023-05-02 +date: 2023-05-03 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 3d9fcd8924f1e..248f2f3dd2ec9 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-helpers plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_random_sampling.devdocs.json b/api_docs/kbn_random_sampling.devdocs.json new file mode 100644 index 0000000000000..d0980a8860449 --- /dev/null +++ b/api_docs/kbn_random_sampling.devdocs.json @@ -0,0 +1,267 @@ +{ + "id": "@kbn/random-sampling", + "client": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/random-sampling", + "id": "def-public.ControlSlider", + "type": "Function", + "tags": [], + "label": "ControlSlider", + "description": [], + "signature": [ + "({\n values,\n currentValue,\n disabled = false,\n disabledReason = '',\n onChange,\n 'data-test-subj': dataTestSubj,\n}: ", + { + "pluginId": "@kbn/random-sampling", + "scope": "public", + "docId": "kibKbnRandomSamplingPluginApi", + "section": "def-public.ControlSliderProps", + "text": "ControlSliderProps" + }, + ") => JSX.Element" + ], + "path": "x-pack/packages/kbn-random-sampling/src/ui/slider_control/index.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/random-sampling", + "id": "def-public.ControlSlider.$1", + "type": "Object", + "tags": [], + "label": "{\n values,\n currentValue,\n disabled = false,\n disabledReason = '',\n onChange,\n 'data-test-subj': dataTestSubj,\n}", + "description": [], + "signature": [ + { + "pluginId": "@kbn/random-sampling", + "scope": "public", + "docId": "kibKbnRandomSamplingPluginApi", + "section": "def-public.ControlSliderProps", + "text": "ControlSliderProps" + } + ], + "path": "x-pack/packages/kbn-random-sampling/src/ui/slider_control/index.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/random-sampling", + "id": "def-public.RandomSamplingIcon", + "type": "Function", + "tags": [], + "label": "RandomSamplingIcon", + "description": [], + "signature": [ + "({ title, titleId, ...props }: ", + { + "pluginId": "@kbn/random-sampling", + "scope": "public", + "docId": "kibKbnRandomSamplingPluginApi", + "section": "def-public.RandomSamplingIconProps", + "text": "RandomSamplingIconProps" + }, + ") => JSX.Element" + ], + "path": "x-pack/packages/kbn-random-sampling/src/ui/icon/sampling_icon.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/random-sampling", + "id": "def-public.RandomSamplingIcon.$1", + "type": "CompoundType", + "tags": [], + "label": "{ title, titleId, ...props }", + "description": [], + "signature": [ + { + "pluginId": "@kbn/random-sampling", + "scope": "public", + "docId": "kibKbnRandomSamplingPluginApi", + "section": "def-public.RandomSamplingIconProps", + "text": "RandomSamplingIconProps" + } + ], + "path": "x-pack/packages/kbn-random-sampling/src/ui/icon/sampling_icon.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "@kbn/random-sampling", + "id": "def-public.ControlSliderProps", + "type": "Interface", + "tags": [], + "label": "ControlSliderProps", + "description": [], + "path": "x-pack/packages/kbn-random-sampling/src/ui/slider_control/index.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/random-sampling", + "id": "def-public.ControlSliderProps.values", + "type": "Array", + "tags": [], + "label": "values", + "description": [ + "Allowed values to show on the Control Slider" + ], + "signature": [ + "number[]" + ], + "path": "x-pack/packages/kbn-random-sampling/src/ui/slider_control/index.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/random-sampling", + "id": "def-public.ControlSliderProps.currentValue", + "type": "number", + "tags": [], + "label": "currentValue", + "description": [ + "Current value set" + ], + "signature": [ + "number | undefined" + ], + "path": "x-pack/packages/kbn-random-sampling/src/ui/slider_control/index.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/random-sampling", + "id": "def-public.ControlSliderProps.disabled", + "type": "CompoundType", + "tags": [], + "label": "disabled", + "description": [ + "When set will show the control in a disabled state" + ], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/packages/kbn-random-sampling/src/ui/slider_control/index.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/random-sampling", + "id": "def-public.ControlSliderProps.disabledReason", + "type": "string", + "tags": [], + "label": "disabledReason", + "description": [ + "An explanation for the disabled state of the control" + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/kbn-random-sampling/src/ui/slider_control/index.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/random-sampling", + "id": "def-public.ControlSliderProps.datatestsubj", + "type": "string", + "tags": [], + "label": "'data-test-subj'", + "description": [ + "A way to pass the test id parameter" + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/kbn-random-sampling/src/ui/slider_control/index.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/random-sampling", + "id": "def-public.ControlSliderProps.onChange", + "type": "Function", + "tags": [], + "label": "onChange", + "description": [ + "A callback for when the slider value changes" + ], + "signature": [ + "(newValue: number) => void" + ], + "path": "x-pack/packages/kbn-random-sampling/src/ui/slider_control/index.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/random-sampling", + "id": "def-public.ControlSliderProps.onChange.$1", + "type": "number", + "tags": [], + "label": "newValue", + "description": [], + "signature": [ + "number" + ], + "path": "x-pack/packages/kbn-random-sampling/src/ui/slider_control/index.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/random-sampling", + "id": "def-public.RandomSamplingIconProps", + "type": "Type", + "tags": [], + "label": "RandomSamplingIconProps", + "description": [], + "signature": [ + "React.SVGProps & CustomProps" + ], + "path": "x-pack/packages/kbn-random-sampling/src/ui/icon/sampling_icon.tsx", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_random_sampling.mdx b/api_docs/kbn_random_sampling.mdx new file mode 100644 index 0000000000000..9451927bd999a --- /dev/null +++ b/api_docs/kbn_random_sampling.mdx @@ -0,0 +1,36 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnRandomSamplingPluginApi +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: 2023-05-03 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/random-sampling'] +--- +import kbnRandomSamplingObj from './kbn_random_sampling.devdocs.json'; + + + +Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 13 | 0 | 7 | 0 | + +## Client + +### Functions + + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index 411585f1dfa57..28fb309cd714c 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-field plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index 0cb00f8d297fc..626a573d3b56e 100644 --- a/api_docs/kbn_repo_file_maps.mdx +++ b/api_docs/kbn_repo_file_maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-file-maps title: "@kbn/repo-file-maps" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-file-maps plugin -date: 2023-05-02 +date: 2023-05-03 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 dac9310471fd6..c92aeeb95e52e 100644 --- a/api_docs/kbn_repo_linter.mdx +++ b/api_docs/kbn_repo_linter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-linter title: "@kbn/repo-linter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-linter plugin -date: 2023-05-02 +date: 2023-05-03 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 edc9dc4e3e43f..e9baf8b225570 100644 --- a/api_docs/kbn_repo_path.mdx +++ b/api_docs/kbn_repo_path.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-path title: "@kbn/repo-path" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-path plugin -date: 2023-05-02 +date: 2023-05-03 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 4c7cdc651523d..c6de08a9a7b07 100644 --- a/api_docs/kbn_repo_source_classifier.mdx +++ b/api_docs/kbn_repo_source_classifier.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-source-classifier title: "@kbn/repo-source-classifier" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-source-classifier plugin -date: 2023-05-02 +date: 2023-05-03 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 f919db7fff5db..d716be2d4c275 100644 --- a/api_docs/kbn_reporting_common.mdx +++ b/api_docs/kbn_reporting_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-common title: "@kbn/reporting-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-common plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-common'] --- import kbnReportingCommonObj from './kbn_reporting_common.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index 07a938cadab4b..2ef816ad62484 100644 --- a/api_docs/kbn_rison.mdx +++ b/api_docs/kbn_rison.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rison title: "@kbn/rison" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rison plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index 9930862386d4b..022c7c269086a 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rule-data-utils plugin -date: 2023-05-02 +date: 2023-05-03 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 b3c4b1ef8d74d..8b76c5402eb2b 100644 --- a/api_docs/kbn_saved_objects_settings.mdx +++ b/api_docs/kbn_saved_objects_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-saved-objects-settings title: "@kbn/saved-objects-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/saved-objects-settings plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/saved-objects-settings'] --- import kbnSavedObjectsSettingsObj from './kbn_saved_objects_settings.devdocs.json'; diff --git a/api_docs/kbn_security_solution_side_nav.mdx b/api_docs/kbn_security_solution_side_nav.mdx index ee3ff5ca959c6..c6a0e7a8523f6 100644 --- a/api_docs/kbn_security_solution_side_nav.mdx +++ b/api_docs/kbn_security_solution_side_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-side-nav title: "@kbn/security-solution-side-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-side-nav plugin -date: 2023-05-02 +date: 2023-05-03 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 6cef2704fd929..53f8d9797575d 100644 --- a/api_docs/kbn_security_solution_storybook_config.mdx +++ b/api_docs/kbn_security_solution_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-storybook-config title: "@kbn/security-solution-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-storybook-config plugin -date: 2023-05-02 +date: 2023-05-03 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 c31b8a3dc5afd..591e469a87e80 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2023-05-02 +date: 2023-05-03 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 de4dbc566881d..fca3cd30604cf 100644 --- a/api_docs/kbn_securitysolution_data_table.mdx +++ b/api_docs/kbn_securitysolution_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-data-table title: "@kbn/securitysolution-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-data-table plugin -date: 2023-05-02 +date: 2023-05-03 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 17b08832f9404..d6aa4378db610 100644 --- a/api_docs/kbn_securitysolution_ecs.mdx +++ b/api_docs/kbn_securitysolution_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-ecs title: "@kbn/securitysolution-ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-ecs plugin -date: 2023-05-02 +date: 2023-05-03 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 52c20172325e8..f0486920aa07d 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-es-utils plugin -date: 2023-05-02 +date: 2023-05-03 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 b6c82f2a51775..b777bdd4fe4a1 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.mdx +++ b/api_docs/kbn_securitysolution_exception_list_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-exception-list-components title: "@kbn/securitysolution-exception-list-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-exception-list-components plugin -date: 2023-05-02 +date: 2023-05-03 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 7d2add4e2aca0..6be11db832d14 100644 --- a/api_docs/kbn_securitysolution_grouping.mdx +++ b/api_docs/kbn_securitysolution_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-grouping title: "@kbn/securitysolution-grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-grouping plugin -date: 2023-05-02 +date: 2023-05-03 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 79d81d97d7b20..da9b1975ee8d8 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2023-05-02 +date: 2023-05-03 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 3ed8a6bafe729..bc140e693167e 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2023-05-02 +date: 2023-05-03 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 9a62675ebcaa4..6c452325f060b 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2023-05-02 +date: 2023-05-03 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 abe2ff0cae395..d01b09fe37522 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2023-05-02 +date: 2023-05-03 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 11cb370d63226..ce186564bdb1d 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2023-05-02 +date: 2023-05-03 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 f10693a14f333..9d3ff52b3aaec 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-api plugin -date: 2023-05-02 +date: 2023-05-03 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 c69122e002d2b..d9aa685039d2d 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-constants plugin -date: 2023-05-02 +date: 2023-05-03 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 5f5683f068f8e..37b636a1f0be0 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2023-05-02 +date: 2023-05-03 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 1935f22b8a3bc..0597dcfa4a63c 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-utils plugin -date: 2023-05-02 +date: 2023-05-03 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 5c814c19dbff9..2d6537bd367c1 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-rules plugin -date: 2023-05-02 +date: 2023-05-03 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 988a06a18bfeb..007fbdca1de89 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-t-grid plugin -date: 2023-05-02 +date: 2023-05-03 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 b63f9d3349c12..cbd6ebdb425c6 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-utils plugin -date: 2023-05-02 +date: 2023-05-03 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 38a4062fa9008..9fa521da2b3ab 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-http-tools plugin -date: 2023-05-02 +date: 2023-05-03 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 358fab4eb6598..0e406e1bbdd0f 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository plugin -date: 2023-05-02 +date: 2023-05-03 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_project_switcher.mdx b/api_docs/kbn_serverless_project_switcher.mdx index 7683a1547c414..da689efc0b3fe 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: 2023-05-02 +date: 2023-05-03 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_storybook_config.mdx b/api_docs/kbn_serverless_storybook_config.mdx index de373dadb7bde..d2ba342236418 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: 2023-05-02 +date: 2023-05-03 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 3a0ebf62bb95e..00cd9d0c304ca 100644 --- a/api_docs/kbn_shared_svg.mdx +++ b/api_docs/kbn_shared_svg.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-svg title: "@kbn/shared-svg" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-svg plugin -date: 2023-05-02 +date: 2023-05-03 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 25dd59d7c8755..35e06badf8de0 100644 --- a/api_docs/kbn_shared_ux_avatar_solution.mdx +++ b/api_docs/kbn_shared_ux_avatar_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-solution title: "@kbn/shared-ux-avatar-solution" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-solution plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-solution'] --- import kbnSharedUxAvatarSolutionObj from './kbn_shared_ux_avatar_solution.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx index 5ad10a8d136a6..444059df69ae7 100644 --- a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx +++ b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-user-profile-components title: "@kbn/shared-ux-avatar-user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-user-profile-components plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-user-profile-components'] --- import kbnSharedUxAvatarUserProfileComponentsObj from './kbn_shared_ux_avatar_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx index 34963126833b3..3db08539e2393 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen title: "@kbn/shared-ux-button-exit-full-screen" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen'] --- import kbnSharedUxButtonExitFullScreenObj from './kbn_shared_ux_button_exit_full_screen.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx index f6d3a80aa114b..e089dd7643087 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen-mocks title: "@kbn/shared-ux-button-exit-full-screen-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen-mocks plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen-mocks'] --- import kbnSharedUxButtonExitFullScreenMocksObj from './kbn_shared_ux_button_exit_full_screen_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index d2eb9b6d86876..b38c931bfe747 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2023-05-02 +date: 2023-05-03 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 39f183b2d936e..6499696de2e3e 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data title: "@kbn/shared-ux-card-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2023-05-02 +date: 2023-05-03 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 602050ae225a5..a7fe523ec06e8 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data-mocks title: "@kbn/shared-ux-card-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 f08ef7f4e21e1..c87e39685e2b7 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: 2023-05-02 +date: 2023-05-03 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_file_context.mdx b/api_docs/kbn_shared_ux_file_context.mdx index 7921fdc8c6c2e..c517d6926b201 100644 --- a/api_docs/kbn_shared_ux_file_context.mdx +++ b/api_docs/kbn_shared_ux_file_context.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-context title: "@kbn/shared-ux-file-context" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-context plugin -date: 2023-05-02 +date: 2023-05-03 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 a7688eac8e75f..6c0b55606e962 100644 --- a/api_docs/kbn_shared_ux_file_image.mdx +++ b/api_docs/kbn_shared_ux_file_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image title: "@kbn/shared-ux-file-image" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image plugin -date: 2023-05-02 +date: 2023-05-03 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 e19974f01f925..54a3e0c891052 100644 --- a/api_docs/kbn_shared_ux_file_image_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_image_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image-mocks title: "@kbn/shared-ux-file-image-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 a9a2f980ad6e5..fece448462aef 100644 --- a/api_docs/kbn_shared_ux_file_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-mocks title: "@kbn/shared-ux-file-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 437359a82cbbd..f6e2d370c3cb2 100644 --- a/api_docs/kbn_shared_ux_file_picker.mdx +++ b/api_docs/kbn_shared_ux_file_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-picker title: "@kbn/shared-ux-file-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-picker plugin -date: 2023-05-02 +date: 2023-05-03 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 baff93574c56d..631bd31451d82 100644 --- a/api_docs/kbn_shared_ux_file_types.mdx +++ b/api_docs/kbn_shared_ux_file_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-types title: "@kbn/shared-ux-file-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-types plugin -date: 2023-05-02 +date: 2023-05-03 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 2a3b643015b68..08ff07f13c84a 100644 --- a/api_docs/kbn_shared_ux_file_upload.mdx +++ b/api_docs/kbn_shared_ux_file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-upload title: "@kbn/shared-ux-file-upload" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-upload plugin -date: 2023-05-02 +date: 2023-05-03 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 c4da37e02340d..48d86cabb4d64 100644 --- a/api_docs/kbn_shared_ux_file_util.mdx +++ b/api_docs/kbn_shared_ux_file_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-util title: "@kbn/shared-ux-file-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-util plugin -date: 2023-05-02 +date: 2023-05-03 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 dd19c240f111c..922b2c96c05c5 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app title: "@kbn/shared-ux-link-redirect-app" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app plugin -date: 2023-05-02 +date: 2023-05-03 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 66068e9e49e0c..19fea05ca91fe 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app-mocks title: "@kbn/shared-ux-link-redirect-app-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 446697c757a70..8fe244dd4a83c 100644 --- a/api_docs/kbn_shared_ux_markdown.mdx +++ b/api_docs/kbn_shared_ux_markdown.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown title: "@kbn/shared-ux-markdown" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown plugin -date: 2023-05-02 +date: 2023-05-03 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 3fae052dba9b6..e9a95cbb78383 100644 --- a/api_docs/kbn_shared_ux_markdown_mocks.mdx +++ b/api_docs/kbn_shared_ux_markdown_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown-mocks title: "@kbn/shared-ux-markdown-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 956617bdc81ed..ff30952c79a7a 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data title: "@kbn/shared-ux-page-analytics-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2023-05-02 +date: 2023-05-03 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 596d96024dc21..29a3de1a752ac 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data-mocks title: "@kbn/shared-ux-page-analytics-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 0afb328bd9017..76f390566fdca 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data title: "@kbn/shared-ux-page-kibana-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2023-05-02 +date: 2023-05-03 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 d273618ad7be3..ef9124c41597f 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data-mocks title: "@kbn/shared-ux-page-kibana-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 3932805002a3f..5e431297a813c 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template title: "@kbn/shared-ux-page-kibana-template" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template plugin -date: 2023-05-02 +date: 2023-05-03 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 09b989575a538..d3e5e25301266 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template-mocks title: "@kbn/shared-ux-page-kibana-template-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 6fb4c3c68a31e..cb81254f30374 100644 --- a/api_docs/kbn_shared_ux_page_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data title: "@kbn/shared-ux-page-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data plugin -date: 2023-05-02 +date: 2023-05-03 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 657c5554953fa..a461527a779c8 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config title: "@kbn/shared-ux-page-no-data-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config plugin -date: 2023-05-02 +date: 2023-05-03 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 80217aeb271b9..7fb7d4cb48a50 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config-mocks title: "@kbn/shared-ux-page-no-data-config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 78b2911646b16..48e46b7b13b10 100644 --- a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-mocks title: "@kbn/shared-ux-page-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 9293c5b62564e..07341a00344c4 100644 --- a/api_docs/kbn_shared_ux_page_solution_nav.mdx +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-solution-nav title: "@kbn/shared-ux-page-solution-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-solution-nav plugin -date: 2023-05-02 +date: 2023-05-03 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 ddc12412abeaa..109a3cb7589e3 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views title: "@kbn/shared-ux-prompt-no-data-views" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2023-05-02 +date: 2023-05-03 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 9b228196718de..c165673de169e 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views-mocks title: "@kbn/shared-ux-prompt-no-data-views-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 db65ff7c9b680..8640050cd8811 100644 --- a/api_docs/kbn_shared_ux_prompt_not_found.mdx +++ b/api_docs/kbn_shared_ux_prompt_not_found.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-not-found title: "@kbn/shared-ux-prompt-not-found" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-not-found plugin -date: 2023-05-02 +date: 2023-05-03 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 3fdcac23b3da7..ee8d3a6300388 100644 --- a/api_docs/kbn_shared_ux_router.mdx +++ b/api_docs/kbn_shared_ux_router.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router title: "@kbn/shared-ux-router" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router plugin -date: 2023-05-02 +date: 2023-05-03 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 205606832f5c7..84a2cb2842f1f 100644 --- a/api_docs/kbn_shared_ux_router_mocks.mdx +++ b/api_docs/kbn_shared_ux_router_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router-mocks title: "@kbn/shared-ux-router-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router-mocks plugin -date: 2023-05-02 +date: 2023-05-03 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 1707ffc183239..edf7e604b0228 100644 --- a/api_docs/kbn_shared_ux_storybook_config.mdx +++ b/api_docs/kbn_shared_ux_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-config title: "@kbn/shared-ux-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-config plugin -date: 2023-05-02 +date: 2023-05-03 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 12407f6b91b8c..ef311ee100eda 100644 --- a/api_docs/kbn_shared_ux_storybook_mock.mdx +++ b/api_docs/kbn_shared_ux_storybook_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-mock title: "@kbn/shared-ux-storybook-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-mock plugin -date: 2023-05-02 +date: 2023-05-03 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 456207f316d19..f4db5ec1478e5 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-utility plugin -date: 2023-05-02 +date: 2023-05-03 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 de9dd13ee8cdd..cf8de373a6dd0 100644 --- a/api_docs/kbn_slo_schema.mdx +++ b/api_docs/kbn_slo_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-slo-schema title: "@kbn/slo-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/slo-schema plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/slo-schema'] --- import kbnSloSchemaObj from './kbn_slo_schema.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index 910a21056b716..10d547b2b7147 100644 --- a/api_docs/kbn_some_dev_log.mdx +++ b/api_docs/kbn_some_dev_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-some-dev-log title: "@kbn/some-dev-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/some-dev-log plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index 5dd384cc654dc..b9598aab5c008 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/std plugin -date: 2023-05-02 +date: 2023-05-03 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 8f7fdafceaebc..d507bd5d1d4e7 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/stdio-dev-helpers plugin -date: 2023-05-02 +date: 2023-05-03 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 1a7fc0217a1d4..8437558a2387e 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/storybook plugin -date: 2023-05-02 +date: 2023-05-03 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 01ddccfc4cbab..1e9dd0f899a00 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/telemetry-tools plugin -date: 2023-05-02 +date: 2023-05-03 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 333718961d5b5..aea398ca02f80 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index ea1b3e2e3ac62..bc6afb7353480 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-jest-helpers plugin -date: 2023-05-02 +date: 2023-05-03 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 0f88448273e0c..8a604e34a1ccc 100644 --- a/api_docs/kbn_test_subj_selector.mdx +++ b/api_docs/kbn_test_subj_selector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-subj-selector title: "@kbn/test-subj-selector" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-subj-selector plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index 8359061ad430e..f2da4b3705364 100644 --- a/api_docs/kbn_tooling_log.mdx +++ b/api_docs/kbn_tooling_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log title: "@kbn/tooling-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/tooling-log plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_ts_projects.mdx b/api_docs/kbn_ts_projects.mdx index 4f3c4f82d43cf..09bfd3a584e7a 100644 --- a/api_docs/kbn_ts_projects.mdx +++ b/api_docs/kbn_ts_projects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ts-projects title: "@kbn/ts-projects" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ts-projects plugin -date: 2023-05-02 +date: 2023-05-03 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 18b4e9a0d57f9..f9d9f7614ad57 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/typed-react-router-config plugin -date: 2023-05-02 +date: 2023-05-03 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 797a853ce52cf..e52c311eb30eb 100644 --- a/api_docs/kbn_ui_actions_browser.mdx +++ b/api_docs/kbn_ui_actions_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-actions-browser title: "@kbn/ui-actions-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-actions-browser plugin -date: 2023-05-02 +date: 2023-05-03 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 052ed7b3024bc..20c02d3235e26 100644 --- a/api_docs/kbn_ui_shared_deps_src.mdx +++ b/api_docs/kbn_ui_shared_deps_src.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-shared-deps-src title: "@kbn/ui-shared-deps-src" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-shared-deps-src plugin -date: 2023-05-02 +date: 2023-05-03 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 af8344844c23e..50c64a509b060 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-theme plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_url_state.mdx b/api_docs/kbn_url_state.mdx index 9ef557a7b78ba..fb874edf725bc 100644 --- a/api_docs/kbn_url_state.mdx +++ b/api_docs/kbn_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-url-state title: "@kbn/url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/url-state plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/url-state'] --- import kbnUrlStateObj from './kbn_url_state.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index cec467c900843..cfaa5038e533f 100644 --- a/api_docs/kbn_user_profile_components.mdx +++ b/api_docs/kbn_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-user-profile-components title: "@kbn/user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/user-profile-components plugin -date: 2023-05-02 +date: 2023-05-03 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 ff7f1e6dd6bed..e4a664c5ec428 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types plugin -date: 2023-05-02 +date: 2023-05-03 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 08f09575ed1f1..36334442a2fff 100644 --- a/api_docs/kbn_utility_types_jest.mdx +++ b/api_docs/kbn_utility_types_jest.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest title: "@kbn/utility-types-jest" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types-jest plugin -date: 2023-05-02 +date: 2023-05-03 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 709e700d1cd69..c64a15a41e8ba 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utils plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index eea162ebd106d..79a4be2a694bc 100644 --- a/api_docs/kbn_yarn_lock_validator.mdx +++ b/api_docs/kbn_yarn_lock_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-yarn-lock-validator title: "@kbn/yarn-lock-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/yarn-lock-validator plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index 2c712c57c8aa3..a7132ec1c4584 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaOverview plugin -date: 2023-05-02 +date: 2023-05-03 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 1fdd3cc0e9620..e28e509e7b527 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaReact plugin -date: 2023-05-02 +date: 2023-05-03 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 4e57ca10ed5f0..fecb8ae5c8e37 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaUtils plugin -date: 2023-05-02 +date: 2023-05-03 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 55177c6869ce0..189284f96aba1 100644 --- a/api_docs/kubernetes_security.mdx +++ b/api_docs/kubernetes_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kubernetesSecurity title: "kubernetesSecurity" image: https://source.unsplash.com/400x175/?github description: API docs for the kubernetesSecurity plugin -date: 2023-05-02 +date: 2023-05-03 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 7fdff437e6653..784cb9a567f48 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lens title: "lens" image: https://source.unsplash.com/400x175/?github description: API docs for the lens plugin -date: 2023-05-02 +date: 2023-05-03 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 f9af1abd4f9e0..62141fdb6ebf6 100644 --- a/api_docs/license_api_guard.mdx +++ b/api_docs/license_api_guard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard title: "licenseApiGuard" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseApiGuard plugin -date: 2023-05-02 +date: 2023-05-03 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 63d875e583def..15f6d7f10f113 100644 --- a/api_docs/license_management.mdx +++ b/api_docs/license_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseManagement title: "licenseManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseManagement plugin -date: 2023-05-02 +date: 2023-05-03 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 5a0aef7870e2e..c02a2f7c293d5 100644 --- a/api_docs/licensing.mdx +++ b/api_docs/licensing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licensing title: "licensing" image: https://source.unsplash.com/400x175/?github description: API docs for the licensing plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index 08dee11d08b74..b7b630c3b1dd5 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lists title: "lists" image: https://source.unsplash.com/400x175/?github description: API docs for the lists plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/management.devdocs.json b/api_docs/management.devdocs.json index 92bdbd0a808d1..4ca34adc10ea7 100644 --- a/api_docs/management.devdocs.json +++ b/api_docs/management.devdocs.json @@ -772,7 +772,40 @@ "path": "src/plugins/management/public/types.ts", "deprecated": false, "trackAdoption": false, - "children": [], + "children": [ + { + "parentPluginId": "management", + "id": "def-public.ManagementStart.setIsSidebarEnabled", + "type": "Function", + "tags": [], + "label": "setIsSidebarEnabled", + "description": [], + "signature": [ + "(enabled: boolean) => void" + ], + "path": "src/plugins/management/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "management", + "id": "def-public.ManagementStart.setIsSidebarEnabled.$1", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [], + "signature": [ + "boolean" + ], + "path": "src/plugins/management/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], "lifecycle": "start", "initialIsOpen": true } diff --git a/api_docs/management.mdx b/api_docs/management.mdx index 920de70b8954a..fc9cc6eb39ae7 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github description: API docs for the management plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'management'] --- import managementObj from './management.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/platform-deployment-management](https://github.com/orgs/elasti | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 41 | 0 | 41 | 6 | +| 43 | 0 | 43 | 6 | ## Client diff --git a/api_docs/maps.mdx b/api_docs/maps.mdx index 1d1a29d3054b1..2883767b53ec9 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github description: API docs for the maps plugin -date: 2023-05-02 +date: 2023-05-03 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 3a75936ef6f49..151039ca33bcc 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github description: API docs for the mapsEms plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index f8f50f62d7823..16de5780c4c2f 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github description: API docs for the ml plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index 1cb7e0cd487e6..8961ac6757ee1 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoring plugin -date: 2023-05-02 +date: 2023-05-03 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 5d8e908117e82..8879d964f2ae4 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoringCollection plugin -date: 2023-05-02 +date: 2023-05-03 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 8fd6537d4ba59..461241d893ee5 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the navigation plugin -date: 2023-05-02 +date: 2023-05-03 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 45933006733e5..33d0a94311f8a 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github description: API docs for the newsfeed plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/notifications.mdx b/api_docs/notifications.mdx index 6deba903c8d00..784134a5ac899 100644 --- a/api_docs/notifications.mdx +++ b/api_docs/notifications.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/notifications title: "notifications" image: https://source.unsplash.com/400x175/?github description: API docs for the notifications plugin -date: 2023-05-02 +date: 2023-05-03 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 85cd1110cadec..d07073486aadd 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github description: API docs for the observability plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/observability_onboarding.mdx b/api_docs/observability_onboarding.mdx index 65b10d6d56ebb..c34b2fa5adf57 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: 2023-05-02 +date: 2023-05-03 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 9c9266b1f4cbd..5a9f9f79348f4 100644 --- a/api_docs/observability_shared.mdx +++ b/api_docs/observability_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityShared title: "observabilityShared" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityShared plugin -date: 2023-05-02 +date: 2023-05-03 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 a9454134b7231..d857fe9c022a9 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github description: API docs for the osquery plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index 5ee5e96b8c416..9c78f9eaba35d 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -7,7 +7,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory description: Directory of public APIs available through plugins or packages. -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -15,13 +15,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Count | Plugins or Packages with a
public API | Number of teams | |--------------|----------|------------------------| -| 611 | 505 | 37 | +| 613 | 507 | 37 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 69562 | 526 | 59996 | 1351 | +| 69608 | 526 | 60031 | 1351 | ## Plugin Directory @@ -120,7 +120,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 117 | 0 | 42 | 10 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 210 | 0 | 94 | 51 | | logstash | [@elastic/logstash](https://github.com/orgs/elastic/teams/logstash) | - | 0 | 0 | 0 | 0 | -| | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 41 | 0 | 41 | 6 | +| | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 43 | 0 | 43 | 6 | | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | - | 269 | 0 | 268 | 29 | | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | - | 67 | 0 | 67 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the machine learning features provided by Elastic. | 257 | 9 | 81 | 41 | @@ -151,10 +151,11 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-reporting-services](https://github.com/orgs/elastic/teams/kibana-reporting-services) | Kibana Screenshotting Plugin | 27 | 0 | 8 | 5 | | searchprofiler | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 0 | 0 | 0 | 0 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides authentication and authorization features, and exposes functionality to understand the capabilities of the currently authenticated user. | 283 | 0 | 94 | 1 | -| | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 117 | 0 | 76 | 27 | +| | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 123 | 0 | 82 | 27 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | The core Serverless plugin, providing APIs to Serverless Project plugins. | 7 | 0 | 6 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Serverless customizations for observability. | 6 | 0 | 6 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Serverless customizations for search. | 6 | 0 | 6 | 0 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Serverless customizations for security. | 6 | 0 | 6 | 0 | | | [@elastic/sec-cloudnative-integrations](https://github.com/orgs/elastic/teams/sec-cloudnative-integrations) | - | 7 | 0 | 7 | 1 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Adds URL Service and sharing capabilities to Kibana | 118 | 0 | 59 | 10 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 22 | 1 | 22 | 1 | @@ -260,7 +261,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 5 | 0 | 0 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 16 | 0 | 7 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 6 | 0 | 6 | 0 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 124 | 0 | 47 | 0 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 136 | 0 | 58 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 3 | 0 | 3 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 8 | 0 | 8 | 0 | @@ -357,7 +358,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 351 | 1 | 5 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 77 | 0 | 55 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 11 | 0 | 11 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 72 | 0 | 52 | 9 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 79 | 0 | 55 | 9 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2 | 0 | 1 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 6 | 0 | 6 | 0 | @@ -467,6 +468,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-performance-testing](https://github.com/orgs/elastic/teams/kibana-performance-testing) | - | 3 | 0 | 3 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 1 | 0 | 1 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 1 | 0 | 1 | 0 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 13 | 0 | 7 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 22 | 0 | 9 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 18 | 0 | 18 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 40 | 0 | 38 | 5 | diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index 917b7732d1e99..9dd86842d7beb 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationUtil plugin -date: 2023-05-02 +date: 2023-05-03 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 df5b312d2218f..efa67379c8b6c 100644 --- a/api_docs/profiling.mdx +++ b/api_docs/profiling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profiling title: "profiling" image: https://source.unsplash.com/400x175/?github description: API docs for the profiling plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index acb39d089d731..d5cdd1bebe434 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github description: API docs for the remoteClusters plugin -date: 2023-05-02 +date: 2023-05-03 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 41deaffdef590..c83bdb3a62c11 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github description: API docs for the reporting plugin -date: 2023-05-02 +date: 2023-05-03 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 73bd59f464b3e..377a4fefe322f 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the rollup plugin -date: 2023-05-02 +date: 2023-05-03 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 0cc7b08fdc7c4..712486721bddd 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github description: API docs for the ruleRegistry plugin -date: 2023-05-02 +date: 2023-05-03 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 e40a16e1c6a7a..15de435a4b486 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github description: API docs for the runtimeFields plugin -date: 2023-05-02 +date: 2023-05-03 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 6b7e01eca21a7..985194658d746 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjects plugin -date: 2023-05-02 +date: 2023-05-03 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 16913ac579c1b..a1a62d0c911cd 100644 --- a/api_docs/saved_objects_finder.mdx +++ b/api_docs/saved_objects_finder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsFinder title: "savedObjectsFinder" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsFinder plugin -date: 2023-05-02 +date: 2023-05-03 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 e97726a5215e1..8f9fc9c15db47 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsManagement plugin -date: 2023-05-02 +date: 2023-05-03 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 1fed9b2e6a235..9d98d8a149ccf 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTagging plugin -date: 2023-05-02 +date: 2023-05-03 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 0b67e19b5bcc0..4ac35b7a11555 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTaggingOss plugin -date: 2023-05-02 +date: 2023-05-03 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 b9296877151e3..3ed58a58ffcaf 100644 --- a/api_docs/saved_search.mdx +++ b/api_docs/saved_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedSearch title: "savedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the savedSearch plugin -date: 2023-05-02 +date: 2023-05-03 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 929b1033263d2..5bf542bf2bd2c 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotMode plugin -date: 2023-05-02 +date: 2023-05-03 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 59a6f89f2d6df..4ad62dbe8e85b 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotting plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index 36bc58ae6e24d..caba439addd79 100644 --- a/api_docs/security.mdx +++ b/api_docs/security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/security title: "security" image: https://source.unsplash.com/400x175/?github description: API docs for the security plugin -date: 2023-05-02 +date: 2023-05-03 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 825b3c48f0f8c..00f734bc8c963 100644 --- a/api_docs/security_solution.devdocs.json +++ b/api_docs/security_solution.devdocs.json @@ -34,7 +34,13 @@ "text": "PluginSetup" }, ", ", - "PluginStart", + { + "pluginId": "securitySolution", + "scope": "public", + "docId": "kibSecuritySolutionPluginApi", + "section": "def-public.PluginStart", + "text": "PluginStart" + }, ", ", "SetupPlugins", ", ", @@ -159,7 +165,13 @@ "<", "StartPluginsDependencies", ", ", - "PluginStart", + { + "pluginId": "securitySolution", + "scope": "public", + "docId": "kibSecuritySolutionPluginApi", + "section": "def-public.PluginStart", + "text": "PluginStart" + }, ">, plugins: ", "SetupPlugins", ") => ", @@ -193,7 +205,13 @@ "<", "StartPluginsDependencies", ", ", - "PluginStart", + { + "pluginId": "securitySolution", + "scope": "public", + "docId": "kibSecuritySolutionPluginApi", + "section": "def-public.PluginStart", + "text": "PluginStart" + }, ">" ], "path": "x-pack/plugins/security_solution/public/plugin.tsx", @@ -237,7 +255,14 @@ }, ", plugins: ", "StartPlugins", - ") => {}" + ") => ", + { + "pluginId": "securitySolution", + "scope": "public", + "docId": "kibSecuritySolutionPluginApi", + "section": "def-public.PluginStart", + "text": "PluginStart" + } ], "path": "x-pack/plugins/security_solution/public/plugin.tsx", "deprecated": false, @@ -1323,6 +1348,70 @@ ], "lifecycle": "setup", "initialIsOpen": true + }, + "start": { + "parentPluginId": "securitySolution", + "id": "def-public.PluginStart", + "type": "Interface", + "tags": [], + "label": "PluginStart", + "description": [], + "path": "x-pack/plugins/security_solution/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "securitySolution", + "id": "def-public.PluginStart.navLinks$", + "type": "Object", + "tags": [], + "label": "navLinks$", + "description": [], + "signature": [ + "Observable", + "<", + "NavigationLink", + "[]>" + ], + "path": "x-pack/plugins/security_solution/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "securitySolution", + "id": "def-public.PluginStart.setIsSidebarEnabled", + "type": "Function", + "tags": [], + "label": "setIsSidebarEnabled", + "description": [], + "signature": [ + "(isSidebarEnabled: boolean) => void" + ], + "path": "x-pack/plugins/security_solution/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "securitySolution", + "id": "def-public.PluginStart.setIsSidebarEnabled.$1", + "type": "boolean", + "tags": [], + "label": "isSidebarEnabled", + "description": [], + "signature": [ + "boolean" + ], + "path": "x-pack/plugins/security_solution/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "lifecycle": "start", + "initialIsOpen": true } }, "server": { @@ -2082,8 +2171,36 @@ "classes": [], "functions": [], "interfaces": [], - "enums": [], + "enums": [ + { + "parentPluginId": "securitySolution", + "id": "def-common.SecurityPageName", + "type": "Enum", + "tags": [], + "label": "SecurityPageName", + "description": [], + "path": "x-pack/plugins/security_solution/common/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], "misc": [ + { + "parentPluginId": "securitySolution", + "id": "def-common.APP_UI_ID", + "type": "string", + "tags": [], + "label": "APP_UI_ID", + "description": [], + "signature": [ + "\"securitySolutionUI\"" + ], + "path": "x-pack/plugins/security_solution/common/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "securitySolution", "id": "def-common.ELASTIC_SECURITY_RULE_ID", diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index 586b5f36cf1d5..8df61c2478f86 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolution title: "securitySolution" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolution plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; @@ -21,13 +21,16 @@ Contact [@elastic/security-solution](https://github.com/orgs/elastic/teams/secur | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 117 | 0 | 76 | 27 | +| 123 | 0 | 82 | 27 | ## Client ### Setup +### Start + + ### Classes @@ -53,6 +56,9 @@ Contact [@elastic/security-solution](https://github.com/orgs/elastic/teams/secur ## Common +### Enums + + ### Consts, variables and types diff --git a/api_docs/serverless.mdx b/api_docs/serverless.mdx index 8e8c5bbc824b9..b29ca7c697b99 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: 2023-05-02 +date: 2023-05-03 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 e96869a3cdd42..ad29b0d4f7046 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: 2023-05-02 +date: 2023-05-03 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 19a2e6bdb38ba..06108daff3d93 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: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessSearch'] --- import serverlessSearchObj from './serverless_search.devdocs.json'; diff --git a/api_docs/serverless_security.devdocs.json b/api_docs/serverless_security.devdocs.json new file mode 100644 index 0000000000000..058c09fd91360 --- /dev/null +++ b/api_docs/serverless_security.devdocs.json @@ -0,0 +1,114 @@ +{ + "id": "serverlessSecurity", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [], + "setup": { + "parentPluginId": "serverlessSecurity", + "id": "def-public.ServerlessSecurityPluginSetup", + "type": "Interface", + "tags": [], + "label": "ServerlessSecurityPluginSetup", + "description": [], + "path": "x-pack/plugins/serverless_security/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "lifecycle": "setup", + "initialIsOpen": true + }, + "start": { + "parentPluginId": "serverlessSecurity", + "id": "def-public.ServerlessSecurityPluginStart", + "type": "Interface", + "tags": [], + "label": "ServerlessSecurityPluginStart", + "description": [], + "path": "x-pack/plugins/serverless_security/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "lifecycle": "start", + "initialIsOpen": true + } + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [], + "setup": { + "parentPluginId": "serverlessSecurity", + "id": "def-server.ServerlessSecurityPluginSetup", + "type": "Interface", + "tags": [], + "label": "ServerlessSecurityPluginSetup", + "description": [], + "path": "x-pack/plugins/serverless_security/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "lifecycle": "setup", + "initialIsOpen": true + }, + "start": { + "parentPluginId": "serverlessSecurity", + "id": "def-server.ServerlessSecurityPluginStart", + "type": "Interface", + "tags": [], + "label": "ServerlessSecurityPluginStart", + "description": [], + "path": "x-pack/plugins/serverless_security/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "lifecycle": "start", + "initialIsOpen": true + } + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [ + { + "parentPluginId": "serverlessSecurity", + "id": "def-common.PLUGIN_ID", + "type": "string", + "tags": [], + "label": "PLUGIN_ID", + "description": [], + "signature": [ + "\"serverlessSecurity\"" + ], + "path": "x-pack/plugins/serverless_security/common/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "serverlessSecurity", + "id": "def-common.PLUGIN_NAME", + "type": "string", + "tags": [], + "label": "PLUGIN_NAME", + "description": [], + "signature": [ + "\"serverlessSecurity\"" + ], + "path": "x-pack/plugins/serverless_security/common/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/serverless_security.mdx b/api_docs/serverless_security.mdx new file mode 100644 index 0000000000000..877017f422a81 --- /dev/null +++ b/api_docs/serverless_security.mdx @@ -0,0 +1,46 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibServerlessSecurityPluginApi +slug: /kibana-dev-docs/api/serverlessSecurity +title: "serverlessSecurity" +image: https://source.unsplash.com/400x175/?github +description: API docs for the serverlessSecurity plugin +date: 2023-05-03 +tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessSecurity'] +--- +import serverlessSecurityObj from './serverless_security.devdocs.json'; + +Serverless customizations for security. + +Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 6 | 0 | 6 | 0 | + +## Client + +### Setup + + +### Start + + +## Server + +### Setup + + +### Start + + +## Common + +### Consts, variables and types + + diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index 47d0bb3669452..3856334d2e949 100644 --- a/api_docs/session_view.mdx +++ b/api_docs/session_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/sessionView title: "sessionView" image: https://source.unsplash.com/400x175/?github description: API docs for the sessionView plugin -date: 2023-05-02 +date: 2023-05-03 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 619e8c7195bf7..cdb7a63466de4 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/share title: "share" image: https://source.unsplash.com/400x175/?github description: API docs for the share plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index 378efbc63371a..1c263f492fa9a 100644 --- a/api_docs/snapshot_restore.mdx +++ b/api_docs/snapshot_restore.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/snapshotRestore title: "snapshotRestore" image: https://source.unsplash.com/400x175/?github description: API docs for the snapshotRestore plugin -date: 2023-05-02 +date: 2023-05-03 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 48837bc217866..d7fc45a1b6d68 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/spaces title: "spaces" image: https://source.unsplash.com/400x175/?github description: API docs for the spaces plugin -date: 2023-05-02 +date: 2023-05-03 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 f29f8b87014d5..2ea2cbe75639c 100644 --- a/api_docs/stack_alerts.mdx +++ b/api_docs/stack_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackAlerts title: "stackAlerts" image: https://source.unsplash.com/400x175/?github description: API docs for the stackAlerts plugin -date: 2023-05-02 +date: 2023-05-03 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 f08d676c37de7..e15a93a28abe6 100644 --- a/api_docs/stack_connectors.mdx +++ b/api_docs/stack_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackConnectors title: "stackConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the stackConnectors plugin -date: 2023-05-02 +date: 2023-05-03 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 27779f9056ce6..e04449544af4b 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/taskManager title: "taskManager" image: https://source.unsplash.com/400x175/?github description: API docs for the taskManager plugin -date: 2023-05-02 +date: 2023-05-03 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 706ea37a4a5b3..60a5f58adc3a0 100644 --- a/api_docs/telemetry.mdx +++ b/api_docs/telemetry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetry title: "telemetry" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetry plugin -date: 2023-05-02 +date: 2023-05-03 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 a83f5d7be0c66..5752437a56229 100644 --- a/api_docs/telemetry_collection_manager.mdx +++ b/api_docs/telemetry_collection_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager title: "telemetryCollectionManager" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionManager plugin -date: 2023-05-02 +date: 2023-05-03 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 5ede2d70c00e1..b7c47e1f0d4bf 100644 --- a/api_docs/telemetry_collection_xpack.mdx +++ b/api_docs/telemetry_collection_xpack.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionXpack title: "telemetryCollectionXpack" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionXpack plugin -date: 2023-05-02 +date: 2023-05-03 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 f123f3a551d60..38e6070682fff 100644 --- a/api_docs/telemetry_management_section.mdx +++ b/api_docs/telemetry_management_section.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection title: "telemetryManagementSection" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryManagementSection plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index b85c65574de6d..d3f8c1544b9c0 100644 --- a/api_docs/threat_intelligence.mdx +++ b/api_docs/threat_intelligence.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/threatIntelligence title: "threatIntelligence" image: https://source.unsplash.com/400x175/?github description: API docs for the threatIntelligence plugin -date: 2023-05-02 +date: 2023-05-03 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 18141dc8921d8..9555c3506824c 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github description: API docs for the timelines plugin -date: 2023-05-02 +date: 2023-05-03 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 a07e5816d033a..68694441a9ce8 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github description: API docs for the transform plugin -date: 2023-05-02 +date: 2023-05-03 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 11b8a4a6671ca..41ab0804ebfda 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github description: API docs for the triggersActionsUi plugin -date: 2023-05-02 +date: 2023-05-03 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 3480152f817b6..595755198644d 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActions plugin -date: 2023-05-02 +date: 2023-05-03 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 5704fbb9e78f8..11205b7ddeefb 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActionsEnhanced plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_field_list.mdx b/api_docs/unified_field_list.mdx index d7cfb5292c5bd..59949026897de 100644 --- a/api_docs/unified_field_list.mdx +++ b/api_docs/unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedFieldList title: "unifiedFieldList" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedFieldList plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedFieldList'] --- import unifiedFieldListObj from './unified_field_list.devdocs.json'; diff --git a/api_docs/unified_histogram.mdx b/api_docs/unified_histogram.mdx index aa20a4d92bc0f..983357fc9c3f4 100644 --- a/api_docs/unified_histogram.mdx +++ b/api_docs/unified_histogram.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedHistogram title: "unifiedHistogram" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedHistogram plugin -date: 2023-05-02 +date: 2023-05-03 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 77d145858c8cb..79b7dc9dc0a91 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch plugin -date: 2023-05-02 +date: 2023-05-03 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 4a8c5d18832f3..07930688590c3 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch.autocomplete plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index b2a1cf083bff9..e55b441c42ccd 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github description: API docs for the urlForwarding plugin -date: 2023-05-02 +date: 2023-05-03 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 11dfce8e2352f..51828f2914265 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the usageCollection plugin -date: 2023-05-02 +date: 2023-05-03 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 7be5a650c050d..f9d06dfeb9137 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github description: API docs for the ux plugin -date: 2023-05-02 +date: 2023-05-03 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 307d067710f0f..6b32e305259fc 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the visDefaultEditor plugin -date: 2023-05-02 +date: 2023-05-03 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 af74917dbfec6..59324f4deb9ac 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeGauge plugin -date: 2023-05-02 +date: 2023-05-03 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 09cd57d039a08..c4838001eac2b 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeHeatmap plugin -date: 2023-05-02 +date: 2023-05-03 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 62fe3f90f14b1..4797a56652fec 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypePie plugin -date: 2023-05-02 +date: 2023-05-03 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 d34ee17761ba2..f72aad7a9ca36 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTable plugin -date: 2023-05-02 +date: 2023-05-03 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 be54eb847be36..4436ef9577f43 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimelion plugin -date: 2023-05-02 +date: 2023-05-03 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 976e3dfc1030e..362d30ed33a62 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimeseries plugin -date: 2023-05-02 +date: 2023-05-03 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 9b9fe5fb9c6ec..3373cf73bb988 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVega plugin -date: 2023-05-02 +date: 2023-05-03 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 e89a82846dad7..6d8cceaf32020 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVislib plugin -date: 2023-05-02 +date: 2023-05-03 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 06b06f7b98bb3..39ceec2a4c492 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeXy plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualization_ui_components.mdx b/api_docs/visualization_ui_components.mdx index 5ca313cc68844..d2ae96e1626e6 100644 --- a/api_docs/visualization_ui_components.mdx +++ b/api_docs/visualization_ui_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizationUiComponents title: "visualizationUiComponents" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizationUiComponents plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizationUiComponents'] --- import visualizationUiComponentsObj from './visualization_ui_components.devdocs.json'; diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index 5bbb6c20b48ed..061ad30f50d74 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizations plugin -date: 2023-05-02 +date: 2023-05-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; diff --git a/config/serverless.security.yml b/config/serverless.security.yml index e906e82191409..f01186a6ceecf 100644 --- a/config/serverless.security.yml +++ b/config/serverless.security.yml @@ -1,2 +1,16 @@ +# Security Project config + +## Disable plugins +enterpriseSearch.enabled: false +xpack.apm.enabled: false +xpack.observability.enabled: false +xpack.uptime.enabled: false + +## Enable the Serverless Security plugin +xpack.serverless.security.enabled: true + +## Set the home route uiSettings.overrides.defaultRoute: /app/security/get_started + +## Set the dev project switcher current type xpack.serverless.plugin.developer.projectSwitcher.currentType: 'security' diff --git a/docs/apm/troubleshooting.asciidoc b/docs/apm/troubleshooting.asciidoc index c4b0994fdc533..339645fe50e5c 100644 --- a/docs/apm/troubleshooting.asciidoc +++ b/docs/apm/troubleshooting.asciidoc @@ -93,8 +93,8 @@ Instead, we should strip away the unique information and group our transactions In this case, that means naming all blog transactions, `/blog`, and all documentation transactions, `/guide`. If you feel like you'd be losing valuable information by following this naming convention, don't fret! -You can always add additional metadata to your transactions using {apm-guide-ref}/metadata.html#labels-fields[labels] (indexed) or -{apm-guide-ref}/metadata.html#custom-fields[custom context] (non-indexed). +You can always add additional metadata to your transactions using {apm-guide-ref}/data-model-metadata.html#data-model-labels[labels] (indexed) or +{apm-guide-ref}/data-model-metadata.html#data-model-custom[custom context] (non-indexed). After ensuring you've correctly named your transactions, you might still see errors in the APM app related to transaction group limit reached: @@ -167,7 +167,7 @@ see <> for further troubleshooting. *Ensure a field is searchable* There are two things you can do to if you'd like to ensure a field is searchable: -1. Index your additional data as {apm-guide-ref}/metadata.html[labels] instead. +1. Index your additional data as {apm-guide-ref}/data-model-metadata.html[labels] instead. These are dynamic by default, which means they will be indexed and become searchable and aggregatable. 2. Create a custom mapping for the field. diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 64b718e955f67..e218387d64341 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -722,6 +722,10 @@ Kibana. |This plugin contains configuration and code used to create a Serverless Search project. It leverages universal configuration and other APIs in the serverless plugin to configure Kibana. +|{kib-repo}blob/{branch}/x-pack/plugins/serverless_security/README.mdx[serverlessSecurity] +|This plugin contains configuration and code used to create a Serverless Security project. It leverages universal configuration and other APIs in the serverless plugin to configure Kibana. + + |{kib-repo}blob/{branch}/x-pack/plugins/session_view/README.md[sessionView] |Session View is meant to provide a visualization into what is going on in a particular Linux environment where the agent is running. It looks likes a terminal emulator; however, it is a tool for introspecting process activity and understanding user and service behaviour in your Linux servers and infrastructure. It is a time-ordered series of process executions displayed in a tree over time. diff --git a/package.json b/package.json index ca0dab0ebd14f..a85349f9841e8 100644 --- a/package.json +++ b/package.json @@ -474,6 +474,7 @@ "@kbn/maps-ems-plugin": "link:src/plugins/maps_ems", "@kbn/maps-plugin": "link:x-pack/plugins/maps", "@kbn/ml-agg-utils": "link:x-pack/packages/ml/agg_utils", + "@kbn/ml-anomaly-utils": "link:x-pack/packages/ml/anomaly_utils", "@kbn/ml-date-picker": "link:x-pack/packages/ml/date_picker", "@kbn/ml-error-utils": "link:x-pack/packages/ml/error_utils", "@kbn/ml-is-defined": "link:x-pack/packages/ml/is_defined", @@ -579,6 +580,7 @@ "@kbn/serverless-observability": "link:x-pack/plugins/serverless_observability", "@kbn/serverless-project-switcher": "link:packages/serverless/project_switcher", "@kbn/serverless-search": "link:x-pack/plugins/serverless_search", + "@kbn/serverless-security": "link:x-pack/plugins/serverless_security", "@kbn/serverless-types": "link:packages/serverless/types", "@kbn/session-notifications-plugin": "link:test/plugin_functional/plugins/session_notifications", "@kbn/session-view-plugin": "link:x-pack/plugins/session_view", @@ -1349,14 +1351,14 @@ "cssnano": "^5.1.12", "cssnano-preset-default": "^5.2.12", "csstype": "^3.0.2", - "cypress": "^12.6.0", - "cypress-axe": "^1.3.0", + "cypress": "^12.10.0", + "cypress-axe": "^1.4.0", "cypress-file-upload": "^5.0.8", - "cypress-multi-reporters": "^1.6.2", + "cypress-multi-reporters": "^1.6.3", "cypress-pipe": "^2.0.0", "cypress-react-selector": "^3.0.0", "cypress-real-events": "^1.7.6", - "cypress-recurse": "^1.27.0", + "cypress-recurse": "^1.31.2", "date-fns": "^2.29.3", "debug": "^2.6.9", "delete-empty": "^2.0.0", @@ -1370,7 +1372,7 @@ "eslint-config-prettier": "^8.5.0", "eslint-module-utils": "^2.6.2", "eslint-plugin-ban": "^1.5.2", - "eslint-plugin-cypress": "^2.12.1", + "eslint-plugin-cypress": "^2.13.2", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-import": "^2.24.2", "eslint-plugin-jest": "^24.5.0", diff --git a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.test.ts b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.test.tsx similarity index 92% rename from packages/core/chrome/core-chrome-browser-internal/src/chrome_service.test.ts rename to packages/core/chrome/core-chrome-browser-internal/src/chrome_service.test.tsx index 0087c5d019f98..0e18b5b72c367 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.test.ts +++ b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.test.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { shallow } from 'enzyme'; +import { shallow, mount } from 'enzyme'; import React from 'react'; import * as Rx from 'rxjs'; import { toArray } from 'rxjs/operators'; @@ -19,6 +19,7 @@ import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks'; import { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks'; import { customBrandingServiceMock } from '@kbn/core-custom-branding-browser-mocks'; import { getAppInfo } from '@kbn/core-application-browser-internal'; +import { findTestSubject } from '@kbn/test-jest-helpers'; import { ChromeService } from './chrome_service'; class FakeApp implements App { @@ -176,6 +177,41 @@ describe('start', () => { // Don't capture the snapshot because it's 600+ lines long. expect(shallow(React.createElement(() => chrome.getHeaderComponent()))).toBeDefined(); }); + + it('renders the default project side navigation', async () => { + const { chrome } = await start(); + + chrome.setChromeStyle('project'); + + const component = mount(chrome.getHeaderComponent()); + + const projectHeader = findTestSubject(component, 'kibanaProjectHeader'); + expect(projectHeader.length).toBe(1); + + const defaultProjectSideNav = findTestSubject(component, 'defaultProjectSideNav'); + expect(defaultProjectSideNav.length).toBe(1); + }); + + it('renders the custom project side navigation', async () => { + const { chrome } = await start(); + + const MyNav = function MyNav() { + return
HELLO
; + }; + chrome.setChromeStyle('project'); + chrome.project.setSideNavComponent(MyNav); + + const component = mount(chrome.getHeaderComponent()); + + const projectHeader = findTestSubject(component, 'kibanaProjectHeader'); + expect(projectHeader.length).toBe(1); + + const defaultProjectSideNav = findTestSubject(component, 'defaultProjectSideNav'); + expect(defaultProjectSideNav.length).toBe(0); // Default side nav not mounted + + const customProjectSideNav = findTestSubject(component, 'customProjectSideNav'); + expect(customProjectSideNav.text()).toBe('HELLO'); + }); }); describe('visibility', () => { 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 41362b3d80dcd..7620b6b5cc153 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 @@ -12,6 +12,7 @@ import { BehaviorSubject, combineLatest, merge, type Observable, of, ReplaySubje import { flatMap, map, takeUntil } from 'rxjs/operators'; import { parse } from 'url'; import { EuiLink } from '@elastic/eui'; +import useObservable from 'react-use/lib/useObservable'; import type { InternalInjectedMetadataStart } from '@kbn/core-injected-metadata-browser-internal'; import type { DocLinksStart } from '@kbn/core-doc-links-browser'; import type { HttpStart } from '@kbn/core-http-browser'; @@ -27,14 +28,17 @@ import type { ChromeHelpExtension, ChromeUserBanner, ChromeStyle, + ChromeProjectNavigation, } from '@kbn/core-chrome-browser'; import type { CustomBrandingStart } from '@kbn/core-custom-branding-browser'; +import type { SideNavComponent as ISideNavComponent } from '@kbn/core-chrome-browser'; import { KIBANA_ASK_ELASTIC_LINK } from './constants'; import { DocTitleService } from './doc_title'; import { NavControlsService } from './nav_controls'; import { NavLinksService } from './nav_links'; +import { ProjectNavigationService } from './project_navigation'; import { RecentlyAccessedService } from './recently_accessed'; -import { Header, ProjectHeader } from './ui'; +import { Header, ProjectHeader, ProjectSideNavigation } from './ui'; import type { InternalChromeStart } from './types'; const IS_LOCKED_KEY = 'core.chrome.isLocked'; @@ -63,6 +67,7 @@ export class ChromeService { private readonly navLinks = new NavLinksService(); private readonly recentlyAccessed = new RecentlyAccessedService(); private readonly docTitle = new DocTitleService(); + private readonly projectNavigation = new ProjectNavigationService(); constructor(private readonly params: ConstructorParams) {} @@ -147,6 +152,7 @@ export class ChromeService { const navControls = this.navControls.start(); const navLinks = this.navLinks.start({ application, http }); + const projectNavigation = this.projectNavigation.start({ application, navLinks }); const recentlyAccessed = await this.recentlyAccessed.start({ http }); const docTitle = this.docTitle.start({ document: window.document }); const { customBranding$ } = customBranding; @@ -170,6 +176,14 @@ export class ChromeService { chromeStyle$.next(style); }; + const setProjectSideNavComponent = (component: ISideNavComponent | null) => { + projectNavigation.setProjectSideNavComponent(component); + }; + + const setProjectNavigation = (config: ChromeProjectNavigation) => { + projectNavigation.setProjectNavigation(config); + }; + const isIE = () => { const ua = window.navigator.userAgent; const msie = ua.indexOf('MSIE '); // IE 10 or older @@ -211,8 +225,28 @@ export class ChromeService { } const getHeaderComponent = () => { - const Component = ({ style$ }: { style$: typeof chromeStyle$ }) => { - if (style$.getValue() === 'project') { + if (chromeStyle$.getValue() === 'project') { + // const projectNavigationConfig = projectNavigation.getProjectNavigation$(); + // TODO: Uncommented when we support the project navigation config + // if (!projectNavigationConfig) { + // throw new Erorr(`Project navigation config must be provided for project.`); + // } + + const projectNavigationComponent$ = projectNavigation.getProjectSideNavComponent$(); + + const ProjectHeaderWithNavigation = () => { + const CustomSideNavComponent = useObservable(projectNavigationComponent$, undefined); + + let SideNavComponent: ISideNavComponent = () => null; + + if (CustomSideNavComponent !== undefined) { + // We have the state from the Observable + SideNavComponent = + CustomSideNavComponent.current !== null + ? CustomSideNavComponent.current + : ProjectSideNavigation; + } + return ( + > + {/* TODO: pass down the SideNavCompProps once they are defined */} + + ); - } - - return ( -
- ); - }; - return ; + }; + + return ; + } + + return ( +
+ ); }; return { @@ -335,6 +373,10 @@ export class ChromeService { getBodyClasses$: () => bodyClasses$.pipe(takeUntil(this.stop$)), setChromeStyle, getChromeStyle$: () => chromeStyle$.pipe(takeUntil(this.stop$)), + project: { + setNavigation: setProjectNavigation, + setSideNavComponent: setProjectSideNavComponent, + }, }; } diff --git a/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/index.ts b/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/index.ts new file mode 100644 index 0000000000000..5485faed25610 --- /dev/null +++ b/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/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 { ProjectNavigationService } from './project_navigation_service'; 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 new file mode 100644 index 0000000000000..97b696c18f6cb --- /dev/null +++ b/packages/core/chrome/core-chrome-browser-internal/src/project_navigation/project_navigation_service.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 { InternalApplicationStart } from '@kbn/core-application-browser-internal'; +import { + ChromeNavLinks, + ChromeProjectNavigation, + SideNavComponent, +} from '@kbn/core-chrome-browser'; +import { BehaviorSubject } from 'rxjs'; + +interface StartDeps { + application: InternalApplicationStart; + navLinks: ChromeNavLinks; +} + +export class ProjectNavigationService { + private customProjectSideNavComponent$ = new BehaviorSubject<{ + current: SideNavComponent | null; + }>({ current: null }); + private projectNavigation$ = new BehaviorSubject(undefined); + + public start({ application, navLinks }: StartDeps) { + // TODO: use application, navLink and projectNavigation$ to: + // 1. validate projectNavigation$ against navLinks, + // 2. filter disabled/missing links from projectNavigation + // 3. keep track of currently active link / path (path will be used to highlight the link in the sidenav and display part of the breadcrumbs) + + return { + setProjectNavigation: (projectNavigation: ChromeProjectNavigation) => { + this.projectNavigation$.next(projectNavigation); + }, + getProjectNavigation$: () => { + return this.projectNavigation$.asObservable(); + }, + setProjectSideNavComponent: (component: SideNavComponent | null) => { + this.customProjectSideNavComponent$.next({ current: component }); + }, + getProjectSideNavComponent$: () => { + return this.customProjectSideNavComponent$.asObservable(); + }, + }; + } +} diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/index.ts b/packages/core/chrome/core-chrome-browser-internal/src/ui/index.ts index 7a5ecadd26f23..89c9b560ad018 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/index.ts +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/index.ts @@ -7,6 +7,6 @@ */ export { Header } from './header'; -export { ProjectHeader } from './project'; +export { ProjectHeader, SideNavigation as ProjectSideNavigation } from './project'; export { LoadingIndicator } from './loading_indicator'; export type { NavType } from './header'; diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx index e85ae262c3bb7..208f3aac7100d 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx @@ -34,12 +34,14 @@ interface Props { kibanaVersion: string; application: InternalApplicationStart; navControlsRight$: Observable; + children: React.ReactNode; } export const ProjectHeader = ({ application, kibanaDocLink, kibanaVersion, + children, ...observables }: Props) => { const renderLogo = () => ( @@ -53,7 +55,7 @@ export const ProjectHeader = ({ return ( <> - + {renderLogo()} @@ -81,9 +83,7 @@ export const ProjectHeader = ({ - - - + {children} ); diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/index.ts b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/index.ts index af18e057731b0..93f6c4ad054ce 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/index.ts +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/index.ts @@ -7,3 +7,4 @@ */ export { ProjectHeader } from './header'; +export { SideNavigation } from './side_navigation'; diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/navigation.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/navigation.tsx index 20549325ec851..445a80cca586d 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/navigation.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/navigation.tsx @@ -11,7 +11,7 @@ import useLocalStorage from 'react-use/lib/useLocalStorage'; import { css } from '@emotion/react'; import { i18n } from '@kbn/i18n'; -import { EuiButtonIcon, EuiCollapsibleNav, EuiThemeProvider, useEuiTheme } from '@elastic/eui'; +import { EuiButtonIcon, EuiCollapsibleNav } from '@elastic/eui'; const LOCAL_STORAGE_IS_OPEN_KEY = 'PROJECT_NAVIGATION_OPEN' as const; const SIZE_OPEN = 248; @@ -33,8 +33,6 @@ const closedAriaLabel = i18n.translate('core.ui.chrome.projectNav.collapsibleNav }); export const ProjectNavigation: React.FC = ({ children }) => { - const { euiTheme, colorMode } = useEuiTheme(); - const [isOpen, setIsOpen] = useLocalStorage(LOCAL_STORAGE_IS_OPEN_KEY, true); const toggleOpen = useCallback(() => { @@ -43,34 +41,31 @@ export const ProjectNavigation: React.FC = ({ children }) => { const collabsibleNavCSS = css` border-inline-end-width: 1, - background: ${euiTheme.colors.darkestShade}, display: flex, flex-direction: row, `; return ( - - - - - } - > - {isOpen && children} - - + + + + } + > + {isOpen && children} + ); }; diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/side_navigation.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/side_navigation.tsx new file mode 100644 index 0000000000000..a76d760170a1a --- /dev/null +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/side_navigation.tsx @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { EuiText } from '@elastic/eui'; +import type { SideNavComponent } from '@kbn/core-chrome-browser'; + +export const SideNavigation: SideNavComponent = () => { + return ( +
+ TODO - Build navigation from config +
+ ); +}; diff --git a/packages/core/chrome/core-chrome-browser-mocks/src/chrome_service.mock.ts b/packages/core/chrome/core-chrome-browser-mocks/src/chrome_service.mock.ts index c7c62c7811277..7025608bc969d 100644 --- a/packages/core/chrome/core-chrome-browser-mocks/src/chrome_service.mock.ts +++ b/packages/core/chrome/core-chrome-browser-mocks/src/chrome_service.mock.ts @@ -63,6 +63,10 @@ const createStartContractMock = () => { getBodyClasses$: jest.fn(), getChromeStyle$: jest.fn(), setChromeStyle: jest.fn(), + project: { + setNavigation: jest.fn(), + setSideNavComponent: jest.fn(), + }, }; startContract.navLinks.getAll.mockReturnValue([]); startContract.getIsVisible$.mockReturnValue(new BehaviorSubject(false)); diff --git a/packages/core/chrome/core-chrome-browser/index.ts b/packages/core/chrome/core-chrome-browser/index.ts index 1d2dca4c957bc..dbf2cc59bb280 100644 --- a/packages/core/chrome/core-chrome-browser/index.ts +++ b/packages/core/chrome/core-chrome-browser/index.ts @@ -29,4 +29,9 @@ export type { ChromeStart, ChromeStyle, ChromeUserBanner, + ChromeProjectNavigationLink, + ChromeProjectNavigation, + ChromeProjectNavigationNode, + SideNavCompProps, + SideNavComponent, } from './src'; diff --git a/packages/core/chrome/core-chrome-browser/src/contracts.ts b/packages/core/chrome/core-chrome-browser/src/contracts.ts index 3f6f756e2d2b1..5930b0e34e341 100644 --- a/packages/core/chrome/core-chrome-browser/src/contracts.ts +++ b/packages/core/chrome/core-chrome-browser/src/contracts.ts @@ -14,7 +14,8 @@ import type { ChromeNavControls } from './nav_controls'; import type { ChromeHelpExtension } from './help_extension'; import type { ChromeBreadcrumb, ChromeBreadcrumbsAppendExtension } from './breadcrumb'; import type { ChromeBadge, ChromeStyle, ChromeUserBanner } from './types'; -import { ChromeGlobalHelpExtensionMenuLink } from './help_extension'; +import type { ChromeGlobalHelpExtensionMenuLink } from './help_extension'; +import type { ChromeProjectNavigation, SideNavComponent } from './project_navigation'; /** * ChromeStart allows plugins to customize the global chrome header UI and @@ -161,4 +162,26 @@ export interface ChromeStart { * Get an observable of the current style type of the chrome. */ getChromeStyle$(): Observable; + /** + * Configuration for serverless projects + */ + project: { + /** + * Sets the project navigation config to be used for rendering project navigation. + * It is used for default project sidenav, project breadcrumbs, tracking active deep link. + * @param projectNavigation The project navigation config + * + * @remarks Has no effect if the chrome style is not `project`. + */ + setNavigation(projectNavigation: ChromeProjectNavigation): void; + + /** + * Set custom project sidenav component to be used instead of the default project sidenav. + * @param getter A function returning a CustomNavigationComponent. + * This component will receive Chrome navigation state as props (not yet implemented) + * + * @remarks Has no effect if the chrome style is not `project`. + */ + setSideNavComponent(component: SideNavComponent | null): void; + }; } diff --git a/packages/core/chrome/core-chrome-browser/src/index.ts b/packages/core/chrome/core-chrome-browser/src/index.ts index 89ba12d616d0e..1810c0ec0bac7 100644 --- a/packages/core/chrome/core-chrome-browser/src/index.ts +++ b/packages/core/chrome/core-chrome-browser/src/index.ts @@ -27,3 +27,11 @@ export type { ChromeRecentlyAccessedHistoryItem, } from './recently_accessed'; export type { ChromeBadge, ChromeUserBanner, ChromeStyle } from './types'; + +export type { + ChromeProjectNavigation, + ChromeProjectNavigationNode, + ChromeProjectNavigationLink, + SideNavCompProps, + SideNavComponent, +} from './project_navigation'; diff --git a/packages/core/chrome/core-chrome-browser/src/project_navigation.ts b/packages/core/chrome/core-chrome-browser/src/project_navigation.ts new file mode 100644 index 0000000000000..da5140d55266c --- /dev/null +++ b/packages/core/chrome/core-chrome-browser/src/project_navigation.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { ComponentType } from 'react'; + +/** @internal */ +type AppId = string; + +/** @internal */ +type DeepLinkId = string; + +/** @internal */ +export type AppDeepLinkId = `${AppId}:${DeepLinkId}`; + +/** @public */ +export type ChromeProjectNavigationLink = AppId | AppDeepLinkId; + +/** @public */ +export interface ChromeProjectNavigationNode { + id?: string; + link?: ChromeProjectNavigationLink; + children?: ChromeProjectNavigationNode[]; + + title?: string; + icon?: string; +} + +/** @public */ +export interface ChromeProjectNavigation { + navigationTree: ChromeProjectNavigationNode[]; +} + +/** @public */ +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface SideNavCompProps { + // TODO: provide the Chrome state to the component through props + // e.g. "navTree", "activeRoute", "recentItems"... +} + +/** @public */ +export type SideNavComponent = ComponentType; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/index.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/index.ts index 981783bb05fd5..759ede0401feb 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/index.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/index.ts @@ -49,13 +49,17 @@ export { modelVersionToVirtualVersion, getModelVersionMapForTypes, getLatestModelVersion, + getCurrentVirtualVersion, + getLatestMigrationVersion, + getVirtualVersionMap, type ModelVersionMap, - compareModelVersions, + type VirtualVersionMap, + compareVirtualVersions, type CompareModelVersionMapParams, type CompareModelVersionStatus, type CompareModelVersionDetails, type CompareModelVersionResult, - getModelVersionsFromMappings, - getModelVersionsFromMappingMeta, + getVirtualVersionsFromMappings, + getVirtualVersionsFromMappingMeta, getModelVersionDelta, } from './src/model_version'; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/index.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/index.ts index ee51fa9aaf4bb..779ff28fb077c 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/index.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/index.ts @@ -9,6 +9,8 @@ export { getTypes, getProperty, getRootProperties, getRootPropertiesObjects } from './lib'; export type { SavedObjectsTypeMappingDefinitions, + V2AlgoIndexMappingMeta, + ZdtAlgoIndexMappingMeta, IndexMappingMeta, IndexMapping, IndexTypesMap, diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/types.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/types.ts index c756f0534db67..a8c74646de1b1 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/types.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/types.ts @@ -59,7 +59,7 @@ export interface IndexMapping { export type IndexTypesMap = Record; /** @internal */ -export interface IndexMappingMeta { +export interface V2AlgoIndexMappingMeta { /** * A dictionary of key -> md5 hash (e.g. 'dashboard': '24234qdfa3aefa3wa') * with each key being a root-level mapping property, and each value being @@ -74,18 +74,22 @@ export interface IndexMappingMeta { * @remark: Only defined for indices using the v2 migration algorithm. */ indexTypesMap?: IndexTypesMap; +} + +/** @internal */ +export interface ZdtAlgoIndexMappingMeta { /** - * The current model versions of the mapping of the index. + * The current virtual version of the mapping of the index. * * @remark: Only defined for indices using the zdt migration algorithm. */ - mappingVersions?: { [k: string]: number }; + mappingVersions?: { [k: string]: string }; /** - * The current model versions of the documents of the index. + * The current virtual versions of the documents of the index. * * @remark: Only defined for indices using the zdt migration algorithm. */ - docVersions?: { [k: string]: number }; + docVersions?: { [k: string]: string }; /** * Info about the current state of the migration. * Should only be present if a migration is in progress or was interrupted. @@ -95,6 +99,9 @@ export interface IndexMappingMeta { migrationState?: IndexMappingMigrationStateMeta; } +/** @internal */ +export type IndexMappingMeta = V2AlgoIndexMappingMeta & ZdtAlgoIndexMappingMeta; + /** @internal */ export interface IndexMappingMigrationStateMeta { /** diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.test.ts index b65bdbce41b7a..3c89894058f57 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.test.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.test.ts @@ -11,6 +11,7 @@ import { virtualVersionToModelVersion, modelVersionToVirtualVersion, assertValidModelVersion, + assertValidVirtualVersion, } from './conversion'; describe('isVirtualModelVersion', () => { @@ -103,3 +104,19 @@ describe('assertValidModelVersion', () => { expect(assertValidModelVersion('3')).toEqual(3); }); }); + +describe('assertValidVirtualVersion', () => { + it('throws if the provided value is not a valid semver', () => { + expect(() => assertValidVirtualVersion('foooo')).toThrowErrorMatchingInlineSnapshot( + `"Virtual versions must be valid semver versions"` + ); + expect(() => assertValidVirtualVersion('1.2.3.4.5.6.7')).toThrowErrorMatchingInlineSnapshot( + `"Virtual versions must be valid semver versions"` + ); + }); + + it('returns the virtual version', () => { + expect(assertValidVirtualVersion('7.17.5')).toEqual('7.17.5'); + expect(assertValidVirtualVersion('10.3.0')).toEqual('10.3.0'); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.ts index c3765ca3c9be9..b10b85084eca5 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/conversion.ts @@ -89,6 +89,14 @@ export const assertValidModelVersion = (modelVersion: string | number): number = return modelVersion; }; +export const assertValidVirtualVersion = (virtualVersion: string): string => { + const semver = Semver.parse(virtualVersion); + if (!semver) { + throw new Error('Virtual versions must be valid semver versions'); + } + return virtualVersion; +}; + const _isVirtualModelVersion = (semver: Semver.SemVer): boolean => { return semver.major === modelVersionVirtualMajor && semver.patch === 0; }; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/get_version_delta.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/get_version_delta.test.ts index 521ddd2a0efc3..4c1bbeaf7f940 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/get_version_delta.test.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/get_version_delta.test.ts @@ -12,12 +12,12 @@ describe('getModelVersionDelta', () => { it('generates an upward delta', () => { const result = getModelVersionDelta({ currentVersions: { - a: 1, - b: 1, + a: '10.1.0', + b: '10.1.0', }, targetVersions: { - a: 2, - b: 3, + a: '10.2.0', + b: '10.3.0', }, deletedTypes: [], }); @@ -26,13 +26,13 @@ describe('getModelVersionDelta', () => { expect(result.diff).toEqual([ { name: 'a', - current: 1, - target: 2, + current: '10.1.0', + target: '10.2.0', }, { name: 'b', - current: 1, - target: 3, + current: '10.1.0', + target: '10.3.0', }, ]); }); @@ -40,12 +40,12 @@ describe('getModelVersionDelta', () => { it('generates a downward delta', () => { const result = getModelVersionDelta({ currentVersions: { - a: 4, - b: 2, + a: '10.4.0', + b: '10.2.0', }, targetVersions: { - a: 1, - b: 1, + a: '10.1.0', + b: '7.17.2', }, deletedTypes: [], }); @@ -54,13 +54,13 @@ describe('getModelVersionDelta', () => { expect(result.diff).toEqual([ { name: 'a', - current: 4, - target: 1, + current: '10.4.0', + target: '10.1.0', }, { name: 'b', - current: 2, - target: 1, + current: '10.2.0', + target: '7.17.2', }, ]); }); @@ -68,12 +68,12 @@ describe('getModelVersionDelta', () => { it('generates a noop delta', () => { const result = getModelVersionDelta({ currentVersions: { - a: 4, - b: 2, + a: '10.4.0', + b: '8.9.2', }, targetVersions: { - a: 4, - b: 2, + a: '10.4.0', + b: '8.9.2', }, deletedTypes: [], }); @@ -85,11 +85,11 @@ describe('getModelVersionDelta', () => { it('ignores deleted types', () => { const result = getModelVersionDelta({ currentVersions: { - a: 1, - b: 3, + a: '10.1.0', + b: '10.3.0', }, targetVersions: { - a: 2, + a: '10.2.0', }, deletedTypes: ['b'], }); @@ -98,8 +98,8 @@ describe('getModelVersionDelta', () => { expect(result.diff).toEqual([ { name: 'a', - current: 1, - target: 2, + current: '10.1.0', + target: '10.2.0', }, ]); }); @@ -108,12 +108,12 @@ describe('getModelVersionDelta', () => { expect(() => getModelVersionDelta({ currentVersions: { - a: 1, - b: 2, + a: '10.1.0', + b: '10.2.0', }, targetVersions: { - a: 2, - b: 1, + a: '10.2.0', + b: '10.1.0', }, deletedTypes: [], }) diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/get_version_delta.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/get_version_delta.ts index f39c52b47f9f7..2426bc4a5770f 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/get_version_delta.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/get_version_delta.ts @@ -6,12 +6,12 @@ * Side Public License, v 1. */ -import type { ModelVersionMap } from './version_map'; -import { compareModelVersions } from './version_compare'; +import type { VirtualVersionMap, VirtualVersion } from './version_map'; +import { compareVirtualVersions } from './version_compare'; interface GetModelVersionDeltaOpts { - currentVersions: ModelVersionMap; - targetVersions: ModelVersionMap; + currentVersions: VirtualVersionMap; + targetVersions: VirtualVersionMap; deletedTypes: string[]; } @@ -26,9 +26,9 @@ interface ModelVersionDeltaTypeResult { /** the name of the type */ name: string; /** the current version the type is at */ - current: number; + current: VirtualVersion; /** the target version the type should go to */ - target: number; + target: VirtualVersion; } /** @@ -41,7 +41,7 @@ export const getModelVersionDelta = ({ targetVersions, deletedTypes, }: GetModelVersionDeltaOpts): ModelVersionDeltaResult => { - const compared = compareModelVersions({ + const compared = compareVirtualVersions({ indexVersions: currentVersions, appVersions: targetVersions, deletedTypes, @@ -78,8 +78,8 @@ const getTypeDelta = ({ targetVersions, }: { type: string; - currentVersions: ModelVersionMap; - targetVersions: ModelVersionMap; + currentVersions: VirtualVersionMap; + targetVersions: VirtualVersionMap; }): ModelVersionDeltaTypeResult => { const currentVersion = currentVersions[type]; const targetVersion = targetVersions[type]; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/index.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/index.ts index 2179199921a82..7279470d26b8f 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/index.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/index.ts @@ -16,17 +16,21 @@ export { export { getModelVersionMapForTypes, getLatestModelVersion, + getCurrentVirtualVersion, + getVirtualVersionMap, + getLatestMigrationVersion, type ModelVersionMap, + type VirtualVersionMap, } from './version_map'; export { - compareModelVersions, + compareVirtualVersions, type CompareModelVersionMapParams, type CompareModelVersionStatus, type CompareModelVersionDetails, type CompareModelVersionResult, } from './version_compare'; export { - getModelVersionsFromMappings, - getModelVersionsFromMappingMeta, + getVirtualVersionsFromMappings, + getVirtualVersionsFromMappingMeta, } from './model_version_from_mappings'; export { getModelVersionDelta } from './get_version_delta'; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.test.ts index 8fea10f11f6b1..ec8d36a53fa9d 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.test.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.test.ts @@ -7,7 +7,7 @@ */ import type { IndexMapping, IndexMappingMeta } from '../mappings'; -import { getModelVersionsFromMappings } from './model_version_from_mappings'; +import { getVirtualVersionsFromMappings } from './model_version_from_mappings'; describe('getModelVersionsFromMappings', () => { const createIndexMapping = (parts: Partial = {}): IndexMapping => ({ @@ -20,41 +20,41 @@ describe('getModelVersionsFromMappings', () => { it('retrieves the version map from docVersions', () => { const mappings = createIndexMapping({ docVersions: { - foo: 3, - bar: 5, + foo: '10.3.0', + bar: '8.16.2', }, }); - const versionMap = getModelVersionsFromMappings({ mappings, source: 'docVersions' }); + const versionMap = getVirtualVersionsFromMappings({ mappings, source: 'docVersions' }); expect(versionMap).toEqual({ - foo: 3, - bar: 5, + foo: '10.3.0', + bar: '8.16.2', }); }); it('retrieves the version map from mappingVersions', () => { const mappings = createIndexMapping({ mappingVersions: { - foo: 2, - bar: 7, + foo: '10.2.0', + bar: '7.17.0', }, }); - const versionMap = getModelVersionsFromMappings({ mappings, source: 'mappingVersions' }); + const versionMap = getVirtualVersionsFromMappings({ mappings, source: 'mappingVersions' }); expect(versionMap).toEqual({ - foo: 2, - bar: 7, + foo: '10.2.0', + bar: '7.17.0', }); }); it('returns undefined for docVersions if meta field is not present', () => { const mappings = createIndexMapping({ mappingVersions: { - foo: 3, - bar: 5, + foo: '10.3.0', + bar: '10.5.0', }, }); - const versionMap = getModelVersionsFromMappings({ mappings, source: 'docVersions' }); + const versionMap = getVirtualVersionsFromMappings({ mappings, source: 'docVersions' }); expect(versionMap).toBeUndefined(); }); @@ -62,11 +62,11 @@ describe('getModelVersionsFromMappings', () => { it('returns undefined for mappingVersions if meta field is not present', () => { const mappings = createIndexMapping({ docVersions: { - foo: 3, - bar: 5, + foo: '10.3.0', + bar: '10.5.0', }, }); - const versionMap = getModelVersionsFromMappings({ mappings, source: 'mappingVersions' }); + const versionMap = getVirtualVersionsFromMappings({ mappings, source: 'mappingVersions' }); expect(versionMap).toBeUndefined(); }); diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.ts index 01fc57d46462b..8dc28969d8476 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/model_version_from_mappings.ts @@ -7,8 +7,8 @@ */ import type { IndexMapping, IndexMappingMeta } from '../mappings'; -import type { ModelVersionMap } from './version_map'; -import { assertValidModelVersion } from './conversion'; +import type { VirtualVersionMap } from './version_map'; +import { assertValidVirtualVersion } from './conversion'; export interface GetModelVersionsFromMappingsOpts { mappings: IndexMapping; @@ -20,16 +20,16 @@ export interface GetModelVersionsFromMappingsOpts { /** * Build the version map from the specified source of the provided mappings. */ -export const getModelVersionsFromMappings = ({ +export const getVirtualVersionsFromMappings = ({ mappings, source, knownTypes, -}: GetModelVersionsFromMappingsOpts): ModelVersionMap | undefined => { +}: GetModelVersionsFromMappingsOpts): VirtualVersionMap | undefined => { if (!mappings._meta) { return undefined; } - return getModelVersionsFromMappingMeta({ + return getVirtualVersionsFromMappingMeta({ meta: mappings._meta, source, knownTypes, @@ -46,20 +46,20 @@ export interface GetModelVersionsFromMappingMetaOpts { /** * Build the version map from the specified source of the provided mappings meta. */ -export const getModelVersionsFromMappingMeta = ({ +export const getVirtualVersionsFromMappingMeta = ({ meta, source, knownTypes, -}: GetModelVersionsFromMappingMetaOpts): ModelVersionMap | undefined => { +}: GetModelVersionsFromMappingMetaOpts): VirtualVersionMap | undefined => { const indexVersions = source === 'mappingVersions' ? meta.mappingVersions : meta.docVersions; if (!indexVersions) { return undefined; } const typeSet = knownTypes ? new Set(knownTypes) : undefined; - return Object.entries(indexVersions).reduce((map, [type, rawVersion]) => { + return Object.entries(indexVersions).reduce((map, [type, rawVersion]) => { if (!typeSet || typeSet.has(type)) { - map[type] = assertValidModelVersion(rawVersion); + map[type] = assertValidVirtualVersion(rawVersion); } return map; }, {}); diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.test.ts index eba6fe1837cce..64feb07455eb3 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.test.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.test.ts @@ -6,18 +6,18 @@ * Side Public License, v 1. */ -import { compareModelVersions } from './version_compare'; +import { compareVirtualVersions } from './version_compare'; describe('compareModelVersions', () => { it('returns the correct value for greater app version', () => { - const result = compareModelVersions({ + const result = compareVirtualVersions({ appVersions: { - foo: 3, - bar: 2, + foo: '10.3.0', + bar: '10.2.0', }, indexVersions: { - foo: 2, - bar: 2, + foo: '10.2.0', + bar: '10.2.0', }, deletedTypes: [], }); @@ -26,14 +26,14 @@ describe('compareModelVersions', () => { }); it('returns the correct value for lesser app version', () => { - const result = compareModelVersions({ + const result = compareVirtualVersions({ appVersions: { - foo: 1, - bar: 2, + foo: '10.1.0', + bar: '10.2.0', }, indexVersions: { - foo: 2, - bar: 2, + foo: '10.2.0', + bar: '10.2.0', }, deletedTypes: [], }); @@ -42,14 +42,14 @@ describe('compareModelVersions', () => { }); it('returns the correct value for equal versions', () => { - const result = compareModelVersions({ + const result = compareVirtualVersions({ appVersions: { - foo: 2, - bar: 2, + foo: '10.2.0', + bar: '10.2.0', }, indexVersions: { - foo: 2, - bar: 2, + foo: '10.2.0', + bar: '10.2.0', }, deletedTypes: [], }); @@ -58,13 +58,13 @@ describe('compareModelVersions', () => { }); it('handles new types not being present in the index', () => { - const result = compareModelVersions({ + const result = compareVirtualVersions({ appVersions: { - foo: 2, - new: 1, + foo: '10.2.0', + bar: '10.1.0', }, indexVersions: { - foo: 2, + foo: '10.2.0', }, deletedTypes: [], }); @@ -73,13 +73,13 @@ describe('compareModelVersions', () => { }); it('handles types not being present in the app', () => { - const result = compareModelVersions({ + const result = compareVirtualVersions({ appVersions: { - foo: 3, + foo: '10.3.0', }, indexVersions: { - foo: 2, - old: 1, + foo: '10.2.0', + old: '10.1.0', }, deletedTypes: [], }); @@ -88,16 +88,16 @@ describe('compareModelVersions', () => { }); it('returns the correct value for conflicts', () => { - const result = compareModelVersions({ + const result = compareVirtualVersions({ appVersions: { - a: 3, - b: 3, - c: 3, + a: '10.3.0', + b: '10.3.0', + c: '10.3.0', }, indexVersions: { - a: 2, - b: 3, - c: 4, + a: '10.2.0', + b: '10.3.0', + c: '10.4.0', }, deletedTypes: [], }); @@ -106,16 +106,16 @@ describe('compareModelVersions', () => { }); it('properly lists the details', () => { - const result = compareModelVersions({ + const result = compareVirtualVersions({ appVersions: { - a: 3, - b: 3, - c: 3, + a: '10.3.0', + b: '10.3.0', + c: '10.3.0', }, indexVersions: { - a: 2, - b: 3, - c: 4, + a: '10.2.0', + b: '10.3.0', + c: '10.4.0', }, deletedTypes: [], }); @@ -126,13 +126,13 @@ describe('compareModelVersions', () => { }); it('ignores deleted types when comparing', () => { - const result = compareModelVersions({ + const result = compareVirtualVersions({ appVersions: { - a: 3, + a: '10.3.0', }, indexVersions: { - a: 2, - b: 3, + a: '10.2.0', + b: '10.3.0', }, deletedTypes: ['b'], }); diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.ts index 9b8d14b7fd862..6d250167cb4e2 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_compare.ts @@ -6,13 +6,14 @@ * Side Public License, v 1. */ -import type { ModelVersionMap } from './version_map'; +import Semver from 'semver'; +import type { VirtualVersionMap } from './version_map'; export interface CompareModelVersionMapParams { /** The latest model version of the types registered in the application */ - appVersions: ModelVersionMap; + appVersions: VirtualVersionMap; /** The model version stored in the index */ - indexVersions: ModelVersionMap; + indexVersions: VirtualVersionMap; /** The list of deleted types to exclude during the compare process */ deletedTypes: string[]; } @@ -37,7 +38,7 @@ export interface CompareModelVersionResult { details: CompareModelVersionDetails; } -export const compareModelVersions = ({ +export const compareVirtualVersions = ({ appVersions, indexVersions, deletedTypes, @@ -53,14 +54,19 @@ export const compareModelVersions = ({ }; allTypes.forEach((type) => { - const appVersion = appVersions[type] ?? 0; - const indexVersion = indexVersions[type] ?? 0; + const appVersion = appVersions[type] ?? '0.0.0'; + const indexVersion = indexVersions[type] ?? '0.0.0'; - if (appVersion > indexVersion) { + const comparison = Semver.compare(appVersion, indexVersion); + + if (comparison > 0) { + // app version greater than index version details.greater.push(type); - } else if (appVersion < indexVersion) { + } else if (comparison < 0) { + // app version lower than index version details.lesser.push(type); } else { + // // app version equal to index version details.equal.push(type); } }); diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.test.ts index aafb83ab96009..1849809c516f9 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.test.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.test.ts @@ -7,7 +7,13 @@ */ import type { SavedObjectsType, SavedObjectsModelVersion } from '@kbn/core-saved-objects-server'; -import { getModelVersionMapForTypes, getLatestModelVersion } from './version_map'; +import { + getModelVersionMapForTypes, + getLatestModelVersion, + getLatestMigrationVersion, + getCurrentVirtualVersion, + getVirtualVersionMap, +} from './version_map'; describe('ModelVersion map utilities', () => { const buildType = (parts: Partial = {}): SavedObjectsType => ({ @@ -24,6 +30,8 @@ describe('ModelVersion map utilities', () => { }, }); + const dummyMigration = jest.fn(); + describe('getLatestModelVersion', () => { it('returns 0 when no model versions are registered', () => { expect(getLatestModelVersion(buildType({ modelVersions: {} }))).toEqual(0); @@ -116,4 +124,141 @@ describe('ModelVersion map utilities', () => { }); }); }); + + describe('getLatestMigrationVersion', () => { + it('returns 0.0.0 when no migrations are registered', () => { + expect(getLatestMigrationVersion(buildType({ migrations: {} }))).toEqual('0.0.0'); + expect(getLatestMigrationVersion(buildType({ migrations: undefined }))).toEqual('0.0.0'); + }); + + it('throws if an invalid version is provided', () => { + expect(() => + getLatestMigrationVersion( + buildType({ + migrations: { + foo: dummyMigration, + '8.6.0': dummyMigration, + }, + }) + ) + ).toThrowError(); + }); + + it('returns the latest registered version', () => { + expect( + getLatestMigrationVersion( + buildType({ + migrations: { + '7.17.2': dummyMigration, + '8.6.0': dummyMigration, + }, + }) + ) + ).toEqual('8.6.0'); + }); + + it('accepts provider functions', () => { + expect( + getLatestMigrationVersion( + buildType({ + migrations: () => ({ + '7.17.2': dummyMigration, + '8.4.0': dummyMigration, + }), + }) + ) + ).toEqual('8.4.0'); + }); + + it('supports unordered maps', () => { + expect( + getLatestMigrationVersion( + buildType({ + migrations: { + '7.17.2': dummyMigration, + '8.7.0': dummyMigration, + '8.2.0': dummyMigration, + }, + }) + ) + ).toEqual('8.7.0'); + }); + }); + + describe('getCurrentVirtualVersion', () => { + it('returns the latest registered migration if switchToModelVersionAt is unset', () => { + expect( + getCurrentVirtualVersion( + buildType({ + migrations: { + '7.17.2': dummyMigration, + '8.6.0': dummyMigration, + }, + modelVersions: { + 1: dummyModelVersion(), + }, + }) + ) + ).toEqual('8.6.0'); + }); + + it('returns the virtual version of the latest model version if switchToModelVersionAt is set', () => { + expect( + getCurrentVirtualVersion( + buildType({ + switchToModelVersionAt: '8.7.0', + migrations: { + '7.17.2': dummyMigration, + '8.6.0': dummyMigration, + }, + modelVersions: { + 1: dummyModelVersion(), + }, + }) + ) + ).toEqual('10.1.0'); + }); + }); + + describe('getVirtualVersionMap', () => { + it('returns the virtual version for each of the provided types', () => { + expect( + getVirtualVersionMap([ + buildType({ + name: 'foo', + switchToModelVersionAt: '8.7.0', + migrations: { + '7.17.2': dummyMigration, + '8.6.0': dummyMigration, + }, + modelVersions: { + 1: dummyModelVersion(), + }, + }), + buildType({ + name: 'bar', + migrations: { + '7.17.2': dummyMigration, + '8.6.0': dummyMigration, + }, + modelVersions: { + 1: dummyModelVersion(), + }, + }), + buildType({ + name: 'dolly', + switchToModelVersionAt: '8.7.0', + migrations: { + '7.17.2': dummyMigration, + '8.6.0': dummyMigration, + }, + }), + ]) + ).toEqual({ + foo: '10.1.0', + bar: '8.6.0', + dolly: '10.0.0', + }); + }); + }); }); diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.ts index dd05e64dbcbef..aafac41df794a 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/model_version/version_map.ts @@ -6,11 +6,32 @@ * Side Public License, v 1. */ +import Semver from 'semver'; import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; -import { assertValidModelVersion } from './conversion'; +import { assertValidModelVersion, modelVersionToVirtualVersion } from './conversion'; +/** + * Represents the virtual version of a given SO type. + * The virtual version is a compatibility format between the old + * migration system's versioning, based on the stack version, and the new model versioning. + * + * A virtual version is a plain semver version. Depending on its major version value, the + * underlying version can be the following: + * - Major < 10: Old migrations system (stack versions), using the equivalent value (e.g `8.7.0` => migration version `8.7.0`) + * - Major == 10: Model versions, using the `10.{modelVersion}.0` format (e.g `10.3.0` => model version 3) + */ +export type VirtualVersion = string; + +/** + * A map of SO type name to Model Version. + */ export type ModelVersionMap = Record; +/** + * A map of SO type name to {@link VirtualVersion}. + */ +export type VirtualVersionMap = Record; + /** * Returns the latest registered model version number for the given type. */ @@ -22,6 +43,14 @@ export const getLatestModelVersion = (type: SavedObjectsType): number => { }, 0); }; +export const getLatestMigrationVersion = (type: SavedObjectsType): string => { + const migrationMap = + typeof type.migrations === 'function' ? type.migrations() : type.migrations ?? {}; + return Object.keys(migrationMap).reduce((memo, current) => { + return Semver.gt(memo, current) ? memo : current; + }, '0.0.0'); +}; + /** * Build a version map for the given types. */ @@ -31,3 +60,29 @@ export const getModelVersionMapForTypes = (types: SavedObjectsType[]): ModelVers return versionMap; }, {}); }; + +/** + * Returns the current virtual version for the given type. + * It will either be the latest model version if the type + * already switched to using them (switchToModelVersionAt is set), + * or the latest migration version for the type otherwise. + */ +export const getCurrentVirtualVersion = (type: SavedObjectsType): string => { + if (type.switchToModelVersionAt) { + const modelVersion = getLatestModelVersion(type); + return modelVersionToVirtualVersion(modelVersion); + } else { + return getLatestMigrationVersion(type); + } +}; + +/** + * Returns a map of virtual model version for the given types. + * See {@link getCurrentVirtualVersion} + */ +export const getVirtualVersionMap = (types: SavedObjectsType[]): VirtualVersionMap => { + return types.reduce((versionMap, type) => { + versionMap[type.name] = getCurrentVirtualVersion(type); + return versionMap; + }, {}); +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/actions/update_index_meta.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/actions/update_index_meta.test.ts index 6ed55ccb49ebe..d597e2927663b 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/actions/update_index_meta.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/actions/update_index_meta.test.ts @@ -17,8 +17,8 @@ describe('updateIndexMeta', () => { const index = '.kibana_1'; const meta: IndexMappingMeta = { mappingVersions: { - foo: 1, - bar: 1, + foo: '10.1.0', + bar: '10.1.0', }, }; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/create_context.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/create_context.ts index c31d4bc799b3b..82234823d973a 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/create_context.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/create_context.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { getModelVersionMapForTypes } from '@kbn/core-saved-objects-base-server-internal'; +import { getVirtualVersionMap } from '@kbn/core-saved-objects-base-server-internal'; import { REMOVED_TYPES } from '../../core'; import type { MigrateIndexOptions } from '../migrate_index'; import type { MigratorContext } from './types'; @@ -33,7 +33,7 @@ export const createContext = ({ kibanaVersion, indexPrefix, types, - typeModelVersions: getModelVersionMapForTypes(types.map((type) => typeRegistry.getType(type)!)), + typeVirtualVersions: getVirtualVersionMap(types.map((type) => typeRegistry.getType(type)!)), elasticsearchClient, typeRegistry, serializer, diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/types.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/types.ts index 95ca7282daf57..58887418eb35a 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/types.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/context/types.ts @@ -12,7 +12,7 @@ import type { ISavedObjectsSerializer, } from '@kbn/core-saved-objects-server'; import type { - ModelVersionMap, + VirtualVersionMap, SavedObjectsMigrationConfigType, } from '@kbn/core-saved-objects-base-server-internal'; import type { DocLinks } from '@kbn/doc-links'; @@ -30,8 +30,8 @@ export interface MigratorContext { readonly indexPrefix: string; /** Name of the types that are living in the index */ readonly types: string[]; - /** Model versions for the registered types */ - readonly typeModelVersions: ModelVersionMap; + /** Virtual versions for the registered types */ + readonly typeVirtualVersions: VirtualVersionMap; /** The client to use for communications with ES */ readonly elasticsearchClient: ElasticsearchClient; /** The maximum number of retries to attempt for a failing action */ 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 d8c176af4be0a..05cb283d378a6 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 @@ -38,7 +38,7 @@ describe('Stage: init', () => { aliases: {}, mappings: { properties: {}, - _meta: { mappingVersions: { foo: 1, bar: 1 } }, + _meta: { mappingVersions: { foo: '10.1.0', bar: '10.1.0' } }, }, settings: {}, }, diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_document_model_version.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_document_model_version.test.ts index c3d3fd67422b9..3595b5fb43cd4 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_document_model_version.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_document_model_version.test.ts @@ -49,8 +49,8 @@ describe('Stage: updateDocumentModelVersion', () => { it('updates state.currentIndexMeta when successful', () => { const state = createState({ currentIndexMeta: { - mappingVersions: { foo: 1, bar: 2 }, - docVersions: { foo: 0, bar: 0 }, + mappingVersions: { foo: '10.1.0', bar: '10.2.0' }, + docVersions: { foo: '0.0.0', bar: '0.0.0' }, migrationState: { convertingDocuments: true, }, @@ -63,8 +63,8 @@ describe('Stage: updateDocumentModelVersion', () => { const newState = updateDocumentModelVersion(state, res, context); expect(newState.currentIndexMeta).toEqual({ - mappingVersions: { foo: 1, bar: 2 }, - docVersions: { foo: 1, bar: 2 }, + mappingVersions: { foo: '10.1.0', bar: '10.2.0' }, + docVersions: { foo: '10.1.0', bar: '10.2.0' }, migrationState: { convertingDocuments: false, }, diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_document_model_version.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_document_model_version.ts index ea3ea142cbd19..94c0923df267d 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_document_model_version.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_document_model_version.ts @@ -24,7 +24,7 @@ export const updateDocumentModelVersion: ModelStage< controlState: 'UPDATE_DOCUMENT_MODEL_VERSIONS_WAIT_FOR_INSTANCES', currentIndexMeta: setMetaDocMigrationComplete({ meta: state.currentIndexMeta, - versions: context.typeModelVersions, + versions: context.typeVirtualVersions, }), }; }; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_mapping_model_version.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_mapping_model_version.test.ts index b21ec69a531f0..ea053570b74e8 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_mapping_model_version.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_mapping_model_version.test.ts @@ -42,7 +42,7 @@ describe('Stage: updateMappingModelVersion', () => { expect(newState.currentIndexMeta).toEqual( setMetaMappingMigrationComplete({ meta: state.currentIndexMeta, - versions: context.typeModelVersions, + versions: context.typeVirtualVersions, }) ); }); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_mapping_model_version.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_mapping_model_version.ts index 946c4a4ab1ef3..54cb5cff6d24a 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_mapping_model_version.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/model/stages/update_mapping_model_version.ts @@ -24,7 +24,7 @@ export const updateMappingModelVersion: ModelStage< controlState: state.aliasActions.length ? 'UPDATE_ALIASES' : 'INDEX_STATE_UPDATE_DONE', currentIndexMeta: setMetaMappingMigrationComplete({ meta: state.currentIndexMeta, - versions: context.typeModelVersions, + versions: context.typeVirtualVersions, }), }; }; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/next.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/next.test.ts index d9135fff65a3e..00684ec46f85c 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/next.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/next.test.ts @@ -85,7 +85,7 @@ describe('actions', () => { expect(setMetaMappingMigrationCompleteMock).toHaveBeenCalledTimes(1); expect(setMetaMappingMigrationCompleteMock).toHaveBeenCalledWith({ meta: state.currentIndexMeta, - versions: context.typeModelVersions, + versions: context.typeVirtualVersions, }); }); @@ -123,7 +123,7 @@ describe('actions', () => { expect(setMetaDocMigrationCompleteMock).toHaveBeenCalledTimes(1); expect(setMetaDocMigrationCompleteMock).toHaveBeenCalledWith({ meta: state.currentIndexMeta, - versions: context.typeModelVersions, + versions: context.typeVirtualVersions, }); }); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/next.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/next.ts index cb3e1b5b5ad27..2e6a785704b8a 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/next.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/next.ts @@ -84,7 +84,7 @@ export const nextActionMap = (context: MigratorContext) => { index: state.currentIndex, meta: setMetaMappingMigrationComplete({ meta: state.currentIndexMeta, - versions: context.typeModelVersions, + versions: context.typeVirtualVersions, }), }), UPDATE_ALIASES: (state: UpdateAliasesState) => @@ -172,7 +172,7 @@ export const nextActionMap = (context: MigratorContext) => { index: state.currentIndex, meta: setMetaDocMigrationComplete({ meta: state.currentIndexMeta, - versions: context.typeModelVersions, + versions: context.typeVirtualVersions, }), }), UPDATE_DOCUMENT_MODEL_VERSIONS_WAIT_FOR_INSTANCES: ( diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/context.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/context.ts index 0dafc36108b93..6fb5eb180d71e 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/context.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/test_helpers/context.ts @@ -31,9 +31,9 @@ export const createContextMock = ( kibanaVersion: '8.7.0', indexPrefix: '.kibana', types: ['foo', 'bar'], - typeModelVersions: { - foo: 1, - bar: 2, + typeVirtualVersions: { + foo: '10.1.0', + bar: '10.2.0', }, documentMigrator: createDocumentMigrator(), migrationConfig: { diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/build_index_mappings.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/build_index_mappings.test.ts index 35354001f6803..6469ebb7f060a 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/build_index_mappings.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/build_index_mappings.test.ts @@ -69,14 +69,14 @@ describe('buildIndexMeta', () => { expect(meta).toEqual({ mappingVersions: { - foo: 2, - bar: 1, - dolly: 3, + foo: '10.2.0', + bar: '10.1.0', + dolly: '10.3.0', }, docVersions: { - foo: 2, - bar: 1, - dolly: 3, + foo: '10.2.0', + bar: '10.1.0', + dolly: '10.3.0', }, migrationState: { convertingDocuments: false, diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/build_index_mappings.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/build_index_mappings.ts index a75ebd4dbdc1e..2157f3798d4db 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/build_index_mappings.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/build_index_mappings.ts @@ -11,7 +11,7 @@ import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; import { type IndexMapping, type IndexMappingMeta, - getModelVersionMapForTypes, + getVirtualVersionMap, } from '@kbn/core-saved-objects-base-server-internal'; import { getBaseMappings, buildTypesMappings } from '../../core'; @@ -48,11 +48,11 @@ interface BuildIndexMetaOpts { * @param types The list of all registered SO types. */ export const buildIndexMeta = ({ types }: BuildIndexMetaOpts): IndexMappingMeta => { - const modelVersions = getModelVersionMapForTypes(types); + const typeVersions = getVirtualVersionMap(types); return { - mappingVersions: modelVersions, - docVersions: modelVersions, + mappingVersions: typeVersions, + docVersions: typeVersions, migrationState: { convertingDocuments: false, }, 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 a0e03552ed946..8d7cbe657758c 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 @@ -6,16 +6,16 @@ * Side Public License, v 1. */ -export const getModelVersionsFromMappingsMock = jest.fn(); -export const compareModelVersionsMock = jest.fn(); -export const getModelVersionMapForTypesMock = jest.fn(); +export const getVirtualVersionsFromMappingsMock = jest.fn(); +export const compareVirtualVersionsMock = jest.fn(); +export const getVirtualVersionMapMock = jest.fn(); jest.doMock('@kbn/core-saved-objects-base-server-internal', () => { const actual = jest.requireActual('@kbn/core-saved-objects-base-server-internal'); return { ...actual, - getModelVersionsFromMappings: getModelVersionsFromMappingsMock, - compareModelVersions: compareModelVersionsMock, - getModelVersionMapForTypes: getModelVersionMapForTypesMock, + getVirtualVersionsFromMappings: getVirtualVersionsFromMappingsMock, + compareVirtualVersions: compareVirtualVersionsMock, + getVirtualVersionMap: getVirtualVersionMapMock, }; }); 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 8430f1f898426..528855417e90f 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 @@ -7,14 +7,14 @@ */ import { - compareModelVersionsMock, - getModelVersionsFromMappingsMock, - getModelVersionMapForTypesMock, + compareVirtualVersionsMock, + getVirtualVersionMapMock, + getVirtualVersionsFromMappingsMock, } from './check_version_compatibility.test.mocks'; import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; import type { IndexMapping, - ModelVersionMap, + VirtualVersionMap, CompareModelVersionResult, } from '@kbn/core-saved-objects-base-server-internal'; import { checkVersionCompatibility } from './check_version_compatibility'; @@ -27,9 +27,9 @@ describe('checkVersionCompatibility', () => { let mappings: IndexMapping; beforeEach(() => { - compareModelVersionsMock.mockReset().mockReturnValue({}); - getModelVersionsFromMappingsMock.mockReset().mockReturnValue({}); - getModelVersionMapForTypesMock.mockReset().mockReturnValue({ status: 'equal' }); + compareVirtualVersionsMock.mockReset().mockReturnValue({}); + getVirtualVersionMapMock.mockReset().mockReturnValue({}); + getVirtualVersionsFromMappingsMock.mockReset().mockReturnValue({ status: 'equal' }); types = [createType({ name: 'foo' }), createType({ name: 'bar' })]; @@ -46,8 +46,8 @@ describe('checkVersionCompatibility', () => { deletedTypes, }); - expect(getModelVersionMapForTypesMock).toHaveBeenCalledTimes(1); - expect(getModelVersionMapForTypesMock).toHaveBeenCalledWith(types); + expect(getVirtualVersionMapMock).toHaveBeenCalledTimes(1); + expect(getVirtualVersionMapMock).toHaveBeenCalledWith(types); }); it('calls getModelVersionsFromMappings with the correct parameters', () => { @@ -58,8 +58,8 @@ describe('checkVersionCompatibility', () => { deletedTypes, }); - expect(getModelVersionsFromMappingsMock).toHaveBeenCalledTimes(1); - expect(getModelVersionsFromMappingsMock).toHaveBeenCalledWith({ + expect(getVirtualVersionsFromMappingsMock).toHaveBeenCalledTimes(1); + expect(getVirtualVersionsFromMappingsMock).toHaveBeenCalledWith({ mappings, source: 'mappingVersions', knownTypes: ['foo', 'bar'], @@ -67,11 +67,11 @@ describe('checkVersionCompatibility', () => { }); it('calls compareModelVersions with the correct parameters', () => { - const appVersions: ModelVersionMap = { foo: 2, bar: 2 }; - const indexVersions: ModelVersionMap = { foo: 1, bar: 1 }; + const appVersions: VirtualVersionMap = { foo: '10.2.0', bar: '10.2.0' }; + const indexVersions: VirtualVersionMap = { foo: '10.1.0', bar: '10.1.0' }; - getModelVersionMapForTypesMock.mockReturnValue(appVersions); - getModelVersionsFromMappingsMock.mockReturnValue(indexVersions); + getVirtualVersionMapMock.mockReturnValue(appVersions); + getVirtualVersionsFromMappingsMock.mockReturnValue(indexVersions); checkVersionCompatibility({ types, @@ -80,8 +80,8 @@ describe('checkVersionCompatibility', () => { deletedTypes, }); - expect(compareModelVersionsMock).toHaveBeenCalledTimes(1); - expect(compareModelVersionsMock).toHaveBeenCalledWith({ + expect(compareVirtualVersionsMock).toHaveBeenCalledTimes(1); + expect(compareVirtualVersionsMock).toHaveBeenCalledWith({ appVersions, indexVersions, deletedTypes, @@ -97,7 +97,7 @@ describe('checkVersionCompatibility', () => { equal: [], }, }; - compareModelVersionsMock.mockReturnValue(expected); + compareVirtualVersionsMock.mockReturnValue(expected); const result = checkVersionCompatibility({ types, 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 c231645fb7993..651875e92c438 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 @@ -8,9 +8,9 @@ import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; import { - getModelVersionsFromMappings, - compareModelVersions, - getModelVersionMapForTypes, + getVirtualVersionsFromMappings, + compareVirtualVersions, + getVirtualVersionMap, type IndexMapping, type CompareModelVersionResult, } from '@kbn/core-saved-objects-base-server-internal'; @@ -28,8 +28,8 @@ export const checkVersionCompatibility = ({ source, deletedTypes, }: CheckVersionCompatibilityOpts): CompareModelVersionResult => { - const appVersions = getModelVersionMapForTypes(types); - const indexVersions = getModelVersionsFromMappings({ + const appVersions = getVirtualVersionMap(types); + const indexVersions = getVirtualVersionsFromMappings({ mappings, source, knownTypes: types.map((type) => type.name), @@ -37,5 +37,5 @@ export const checkVersionCompatibility = ({ if (!indexVersions) { throw new Error(`Cannot check version: ${source} not present in the mapping meta`); } - return compareModelVersions({ appVersions, indexVersions, deletedTypes }); + return compareVirtualVersions({ appVersions, indexVersions, deletedTypes }); }; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/generate_additive_mapping_diff.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/generate_additive_mapping_diff.test.ts index ce0cba20f427c..9e711dd6daaf1 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,6 +6,7 @@ * Side Public License, v 1. */ +import type { SavedObjectsModelVersion } from '@kbn/core-saved-objects-server'; import type { IndexMappingMeta } from '@kbn/core-saved-objects-base-server-internal'; import { generateAdditiveMappingDiff } from './generate_additive_mapping_diff'; import { createType } from '../test_helpers'; @@ -13,20 +14,24 @@ import { createType } from '../test_helpers'; describe('generateAdditiveMappingDiff', () => { const deletedTypes = ['deletedType']; + const stubMigration = jest.fn(); + const stubModelVersion: SavedObjectsModelVersion = { modelChange: { type: 'expansion' } }; + const getTypes = () => { const foo = createType({ name: 'foo', + switchToModelVersionAt: '8.0.0', modelVersions: { - 1: { modelChange: { type: 'expansion' } }, - 2: { modelChange: { type: 'expansion' } }, + 1: stubModelVersion, + 2: stubModelVersion, }, mappings: { properties: { fooProp: { type: 'text' } } }, }); const bar = createType({ name: 'bar', - modelVersions: { - 1: { modelChange: { type: 'expansion' } }, - 2: { modelChange: { type: 'expansion' } }, + migrations: { + '8.0.0': stubMigration, + '8.5.0': stubMigration, }, mappings: { properties: { barProp: { type: 'text' } } }, }); @@ -39,8 +44,8 @@ describe('generateAdditiveMappingDiff', () => { const types = [foo, bar]; const meta: IndexMappingMeta = { mappingVersions: { - foo: 1, - bar: 1, + foo: '10.1.0', + bar: '7.9.0', }, }; @@ -61,8 +66,8 @@ describe('generateAdditiveMappingDiff', () => { const types = [foo, bar]; const meta: IndexMappingMeta = { mappingVersions: { - foo: 1, - bar: 2, + foo: '10.1.0', + bar: '8.5.0', }, }; @@ -82,9 +87,9 @@ describe('generateAdditiveMappingDiff', () => { const types = [foo, bar]; const meta: IndexMappingMeta = { mappingVersions: { - foo: 1, - bar: 1, - deletedType: 42, + foo: '10.1.0', + bar: '8.2.0', + deletedType: '10.2.0', }, }; @@ -105,8 +110,8 @@ describe('generateAdditiveMappingDiff', () => { const types = [foo, bar]; const meta: IndexMappingMeta = { mappingVersions: { - foo: 1, - bar: 3, + foo: '10.1.0', + bar: '10.1.0', }, }; 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 400e01e999797..0ef7437b2d6c3 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 @@ -12,8 +12,8 @@ import type { } from '@kbn/core-saved-objects-server'; import { IndexMappingMeta, - getModelVersionsFromMappingMeta, - getModelVersionMapForTypes, + getVirtualVersionsFromMappingMeta, + getVirtualVersionMap, getModelVersionDelta, } from '@kbn/core-saved-objects-base-server-internal'; @@ -35,8 +35,8 @@ export const generateAdditiveMappingDiff = ({ meta, deletedTypes, }: GenerateAdditiveMappingsDiffOpts): SavedObjectsMappingProperties => { - const typeVersions = getModelVersionMapForTypes(types); - const mappingVersion = getModelVersionsFromMappingMeta({ + const typeVersions = getVirtualVersionMap(types); + const mappingVersion = getVirtualVersionsFromMappingMeta({ meta, source: 'mappingVersions', knownTypes: types.map((type) => type.name), diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/outdated_documents_query.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/outdated_documents_query.test.ts index f39016b7d86a1..fb72061735eff 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/outdated_documents_query.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/outdated_documents_query.test.ts @@ -16,10 +16,13 @@ const dummyModelVersion: SavedObjectsModelVersion = { }, }; +const dummyMigration = jest.fn(); + describe('getOutdatedDocumentsQuery', () => { - it('generates the correct query', () => { + it('generates the correct query for types using model versions', () => { const fooType = createType({ name: 'foo', + switchToModelVersionAt: '8.8.0', modelVersions: { 1: dummyModelVersion, 2: dummyModelVersion, @@ -27,6 +30,7 @@ describe('getOutdatedDocumentsQuery', () => { }); const barType = createType({ name: 'bar', + switchToModelVersionAt: '8.8.0', modelVersions: { 1: dummyModelVersion, 2: dummyModelVersion, @@ -50,40 +54,11 @@ describe('getOutdatedDocumentsQuery', () => { "type": "foo", }, }, + ], + "must_not": Array [ Object { - "bool": Object { - "should": Array [ - Object { - "bool": Object { - "must": Object { - "exists": Object { - "field": "migrationVersion", - }, - }, - "must_not": Object { - "term": Object { - "migrationVersion.foo": "10.2.0", - }, - }, - }, - }, - Object { - "bool": Object { - "must_not": Array [ - Object { - "exists": Object { - "field": "migrationVersion", - }, - }, - Object { - "term": Object { - "typeMigrationVersion": "10.2.0", - }, - }, - ], - }, - }, - ], + "term": Object { + "typeMigrationVersion": "10.2.0", }, }, ], @@ -97,40 +72,148 @@ describe('getOutdatedDocumentsQuery', () => { "type": "bar", }, }, + ], + "must_not": Array [ Object { - "bool": Object { - "should": Array [ - Object { - "bool": Object { - "must": Object { - "exists": Object { - "field": "migrationVersion", - }, - }, - "must_not": Object { - "term": Object { - "migrationVersion.bar": "10.3.0", - }, - }, - }, - }, - Object { - "bool": Object { - "must_not": Array [ - Object { - "exists": Object { - "field": "migrationVersion", - }, - }, - Object { - "term": Object { - "typeMigrationVersion": "10.3.0", - }, - }, - ], - }, - }, - ], + "term": Object { + "typeMigrationVersion": "10.3.0", + }, + }, + ], + }, + }, + ], + }, + } + `); + }); + + it('generates the correct query for types still using old migrations', () => { + const fooType = createType({ + name: 'foo', + migrations: { + '7.17.2': dummyMigration, + '8.5.0': dummyMigration, + }, + }); + const barType = createType({ + name: 'bar', + migrations: () => ({ + '7.15.5': dummyMigration, + '8.7.2': dummyMigration, + }), + }); + + const query = getOutdatedDocumentsQuery({ + types: [fooType, barType], + }); + + expect(query).toMatchInlineSnapshot(` + Object { + "bool": Object { + "should": Array [ + Object { + "bool": Object { + "must": Array [ + Object { + "term": Object { + "type": "foo", + }, + }, + ], + "must_not": Array [ + Object { + "term": Object { + "typeMigrationVersion": "8.5.0", + }, + }, + ], + }, + }, + Object { + "bool": Object { + "must": Array [ + Object { + "term": Object { + "type": "bar", + }, + }, + ], + "must_not": Array [ + Object { + "term": Object { + "typeMigrationVersion": "8.7.2", + }, + }, + ], + }, + }, + ], + }, + } + `); + }); + + it('generates the correct query for mixed types', () => { + const fooType = createType({ + name: 'foo', + migrations: { + '7.17.2': dummyMigration, + '8.5.0': dummyMigration, + }, + switchToModelVersionAt: '8.8.0', + modelVersions: { + 1: dummyModelVersion, + 2: dummyModelVersion, + }, + }); + const barType = createType({ + name: 'bar', + migrations: () => ({ + '7.15.5': dummyMigration, + '8.7.2': dummyMigration, + }), + }); + + const query = getOutdatedDocumentsQuery({ + types: [fooType, barType], + }); + + expect(query).toMatchInlineSnapshot(` + Object { + "bool": Object { + "should": Array [ + Object { + "bool": Object { + "must": Array [ + Object { + "term": Object { + "type": "foo", + }, + }, + ], + "must_not": Array [ + Object { + "term": Object { + "typeMigrationVersion": "10.2.0", + }, + }, + ], + }, + }, + Object { + "bool": Object { + "must": Array [ + Object { + "term": Object { + "type": "bar", + }, + }, + ], + "must_not": Array [ + Object { + "term": Object { + "typeMigrationVersion": "8.7.2", }, }, ], diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/outdated_documents_query.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/outdated_documents_query.ts index e15a2e447b554..40495654e6146 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/outdated_documents_query.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/outdated_documents_query.ts @@ -8,10 +8,7 @@ import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; -import { - getModelVersionMapForTypes, - modelVersionToVirtualVersion, -} from '@kbn/core-saved-objects-base-server-internal'; +import { getVirtualVersionMap } from '@kbn/core-saved-objects-base-server-internal'; interface GetOutdatedDocumentsQueryOps { types: SavedObjectsType[]; @@ -23,36 +20,15 @@ export const getOutdatedDocumentsQuery = ({ // Note: in theory, we could check the difference of model version with the index's // and narrow the search filter only on the type that have different versions. // however, it feels safer to just search for all outdated document, just in case. - const modelVersions = getModelVersionMapForTypes(types); + const virtualVersions = getVirtualVersionMap(types); return { bool: { should: types.map((type) => { - const virtualVersion = modelVersionToVirtualVersion(modelVersions[type.name]); + const virtualVersion = virtualVersions[type.name]; return { bool: { - must: [ - { term: { type: type.name } }, - { - bool: { - should: [ - { - bool: { - must: { exists: { field: 'migrationVersion' } }, - must_not: { term: { [`migrationVersion.${type.name}`]: virtualVersion } }, - }, - }, - { - bool: { - must_not: [ - { exists: { field: 'migrationVersion' } }, - { term: { typeMigrationVersion: virtualVersion } }, - ], - }, - }, - ], - }, - }, - ], + must: [{ term: { type: type.name } }], + must_not: [{ term: { typeMigrationVersion: virtualVersion } }], }, }; }), diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/update_index_meta.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/update_index_meta.test.ts index 4298c6b027072..da9654400bf6f 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/update_index_meta.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/update_index_meta.test.ts @@ -8,7 +8,7 @@ import type { IndexMappingMeta, - ModelVersionMap, + VirtualVersionMap, } from '@kbn/core-saved-objects-base-server-internal'; import { setMetaDocMigrationStarted, @@ -18,12 +18,12 @@ import { const getDefaultMeta = (): IndexMappingMeta => ({ mappingVersions: { - foo: 1, - bar: 1, + foo: '10.1.0', + bar: '10.1.0', }, docVersions: { - foo: 1, - bar: 1, + foo: '10.1.0', + bar: '10.1.0', }, migrationState: { convertingDocuments: false, @@ -33,7 +33,7 @@ const getDefaultMeta = (): IndexMappingMeta => ({ describe('setMetaMappingMigrationComplete', () => { it('updates the meta to set the mappingVersions', () => { const meta: IndexMappingMeta = getDefaultMeta(); - const versions: ModelVersionMap = { foo: 3, bar: 2 }; + const versions: VirtualVersionMap = { foo: '10.3.0', bar: '10.2.0' }; const updated = setMetaMappingMigrationComplete({ meta, versions }); @@ -67,7 +67,7 @@ describe('setMetaDocMigrationComplete', () => { convertingDocuments: true, }, }; - const versions: ModelVersionMap = { foo: 3, bar: 2 }; + const versions: VirtualVersionMap = { foo: '10.3.0', bar: '10.2.0' }; const updated = setMetaDocMigrationComplete({ meta, versions }); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/update_index_meta.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/update_index_meta.ts index 73d693d4a1f1a..2d06f5adcb378 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/update_index_meta.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/zdt/utils/update_index_meta.ts @@ -8,7 +8,7 @@ import type { IndexMappingMeta, - ModelVersionMap, + VirtualVersionMap, } from '@kbn/core-saved-objects-base-server-internal'; export const setMetaMappingMigrationComplete = ({ @@ -16,7 +16,7 @@ export const setMetaMappingMigrationComplete = ({ versions, }: { meta: IndexMappingMeta; - versions: ModelVersionMap; + versions: VirtualVersionMap; }): IndexMappingMeta => { return { ...meta, @@ -44,7 +44,7 @@ export const setMetaDocMigrationComplete = ({ versions, }: { meta: IndexMappingMeta; - versions: ModelVersionMap; + versions: VirtualVersionMap; }): IndexMappingMeta => { return { ...meta, diff --git a/packages/kbn-cell-actions/src/hooks/use_data_grid_column_cell_actions.test.tsx b/packages/kbn-cell-actions/src/hooks/use_data_grid_column_cell_actions.test.tsx index 7cb321a6a3f67..0ffc68615c0b5 100644 --- a/packages/kbn-cell-actions/src/hooks/use_data_grid_column_cell_actions.test.tsx +++ b/packages/kbn-cell-actions/src/hooks/use_data_grid_column_cell_actions.test.tsx @@ -191,4 +191,32 @@ describe('useDataGridColumnsCellActions', () => { expect(mockCloseCellPopover).toHaveBeenCalled(); }); }); + + it('should return empty array of actions when list of fields is empty', async () => { + const { result, waitForNextUpdate } = renderHook(useDataGridColumnsCellActions, { + initialProps: { + ...useDataGridColumnsCellActionsProps, + fields: [], + }, + }); + + await waitForNextUpdate(); + + expect(result.current).toBeInstanceOf(Array); + expect(result.current.length).toBe(0); + }); + + it('should return empty array of actions when list of fields is undefined', async () => { + const { result, waitForNextUpdate } = renderHook(useDataGridColumnsCellActions, { + initialProps: { + ...useDataGridColumnsCellActionsProps, + fields: undefined, + }, + }); + + await waitForNextUpdate(); + + expect(result.current).toBeInstanceOf(Array); + expect(result.current.length).toBe(0); + }); }); diff --git a/packages/kbn-cell-actions/src/hooks/use_data_grid_column_cell_actions.tsx b/packages/kbn-cell-actions/src/hooks/use_data_grid_column_cell_actions.tsx index 344e4a058ad44..2913004218032 100644 --- a/packages/kbn-cell-actions/src/hooks/use_data_grid_column_cell_actions.tsx +++ b/packages/kbn-cell-actions/src/hooks/use_data_grid_column_cell_actions.tsx @@ -30,7 +30,7 @@ interface BulkField extends Pick { export interface UseDataGridColumnsCellActionsProps extends Pick { - fields: BulkField[]; + fields?: BulkField[]; dataGridRef: MutableRefObject; } export type UseDataGridColumnsCellActions< @@ -46,11 +46,11 @@ export const useDataGridColumnsCellActions: UseDataGridColumnsCellActions = ({ }) => { const bulkContexts: CellActionCompatibilityContext[] = useMemo( () => - fields.map(({ values, ...field }) => ({ + fields?.map(({ values, ...field }) => ({ field, // we are getting the actions for the whole column field, so the compatibility check will be done without the value trigger: { id: triggerId }, metadata, - })), + })) ?? [], [fields, triggerId, metadata] ); @@ -60,11 +60,13 @@ export const useDataGridColumnsCellActions: UseDataGridColumnsCellActions = ({ const columnsCellActions = useMemo(() => { if (loading) { - return fields.map(() => [ - () => , - ]); + return ( + fields?.map(() => [ + () => , + ]) ?? [] + ); } - if (!columnsActions) { + if (!columnsActions || !fields || fields.length === 0) { return []; } return columnsActions.map((actions, columnIndex) => diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 85c8b6c888236..13cf535a36873 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -118,6 +118,7 @@ pageLoadAssetSize: serverless: 16573 serverlessObservability: 16582 serverlessSearch: 17548 + serverlessSecurity: 41807 sessionView: 77750 share: 71239 snapshotRestore: 79032 diff --git a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/base.fixtures.ts b/src/core/server/integration_tests/saved_objects/migrations/fixtures/zdt_base.fixtures.ts similarity index 87% rename from src/core/server/integration_tests/saved_objects/migrations/zero_downtime/base.fixtures.ts rename to src/core/server/integration_tests/saved_objects/migrations/fixtures/zdt_base.fixtures.ts index 3cf5e499eaa6e..53c7c1d11613d 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/base.fixtures.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/fixtures/zdt_base.fixtures.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { SavedObjectsModelVersion } from '@kbn/core-saved-objects-server'; +import { SavedObjectsModelVersion, SavedObjectMigrationFn } from '@kbn/core-saved-objects-server'; import { createType } from '../test_utils'; import { type KibanaMigratorTestKitParams } from '../kibana_migrator_test_kit'; @@ -29,6 +29,8 @@ export const dummyModelVersion: SavedObjectsModelVersion = { }, }; +export const noopMigration: SavedObjectMigrationFn = (doc) => doc; + export const getFooType = () => { return createType({ name: 'foo', @@ -130,3 +132,18 @@ export const getExcludedType = () => { }, }); }; + +export const getLegacyType = () => { + return createType({ + name: 'legacy', + mappings: { + properties: { + someField: { type: 'text' }, + }, + }, + migrations: { + '7.0.0': noopMigration, + '7.5.0': noopMigration, + }, + }); +}; diff --git a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/basic_document_migration.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/basic_document_migration.test.ts similarity index 97% rename from src/core/server/integration_tests/saved_objects/migrations/zero_downtime/basic_document_migration.test.ts rename to src/core/server/integration_tests/saved_objects/migrations/zdt_1/basic_document_migration.test.ts index 418adb0a7894e..9122cb1bd6c10 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/basic_document_migration.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/basic_document_migration.test.ts @@ -14,7 +14,11 @@ import { SavedObjectsBulkCreateObject } from '@kbn/core-saved-objects-api-server import '../jest_matchers'; import { getKibanaMigratorTestKit } from '../kibana_migrator_test_kit'; import { delay, parseLogFile } from '../test_utils'; -import { getBaseMigratorParams, getSampleAType, getSampleBType } from './base.fixtures'; +import { + getBaseMigratorParams, + getSampleAType, + getSampleBType, +} from '../fixtures/zdt_base.fixtures'; export const logFilePath = Path.join(__dirname, 'basic_document_migration.test.log'); @@ -179,8 +183,8 @@ describe('ZDT upgrades - basic document migration', () => { ); expect(mappingMeta.docVersions).toEqual({ - sample_a: 2, - sample_b: 3, + sample_a: '10.2.0', + sample_b: '10.3.0', }); const { saved_objects: sampleADocs } = await savedObjectsRepository.find({ type: 'sample_a' }); diff --git a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/conversion_failures.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/conversion_failures.test.ts similarity index 98% rename from src/core/server/integration_tests/saved_objects/migrations/zero_downtime/conversion_failures.test.ts rename to src/core/server/integration_tests/saved_objects/migrations/zdt_1/conversion_failures.test.ts index 4f1b0a4bfe468..750753d52c66b 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/conversion_failures.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/conversion_failures.test.ts @@ -14,7 +14,11 @@ import { SavedObjectsBulkCreateObject } from '@kbn/core-saved-objects-api-server import '../jest_matchers'; import { getKibanaMigratorTestKit } from '../kibana_migrator_test_kit'; import { delay, parseLogFile } from '../test_utils'; -import { getBaseMigratorParams, getSampleAType, getSampleBType } from './base.fixtures'; +import { + getBaseMigratorParams, + getSampleAType, + getSampleBType, +} from '../fixtures/zdt_base.fixtures'; export const logFilePath = Path.join(__dirname, 'conversion_failures.test.log'); diff --git a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/create_index.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/create_index.test.ts similarity index 96% rename from src/core/server/integration_tests/saved_objects/migrations/zero_downtime/create_index.test.ts rename to src/core/server/integration_tests/saved_objects/migrations/zdt_1/create_index.test.ts index 760b0f113a8ff..18b9aecc39cad 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/create_index.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/create_index.test.ts @@ -12,7 +12,7 @@ import '../jest_matchers'; import { createTestServers, type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; import { getKibanaMigratorTestKit } from '../kibana_migrator_test_kit'; import { delay, parseLogFile } from '../test_utils'; -import { getBaseMigratorParams, getFooType, getBarType } from './base.fixtures'; +import { getBaseMigratorParams, getFooType, getBarType } from '../fixtures/zdt_base.fixtures'; export const logFilePath = Path.join(__dirname, 'create_index.test.log'); @@ -81,12 +81,12 @@ describe('ZDT upgrades - running on a fresh cluster', () => { expect(mappingMeta).toEqual({ docVersions: { - foo: 2, - bar: 1, + foo: '10.2.0', + bar: '10.1.0', }, mappingVersions: { - foo: 2, - bar: 1, + foo: '10.2.0', + bar: '10.1.0', }, migrationState: expect.objectContaining({ convertingDocuments: false, diff --git a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/document_cleanup.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/document_cleanup.test.ts similarity index 98% rename from src/core/server/integration_tests/saved_objects/migrations/zero_downtime/document_cleanup.test.ts rename to src/core/server/integration_tests/saved_objects/migrations/zdt_1/document_cleanup.test.ts index b321a400684b7..98e85b3029f18 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/document_cleanup.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/document_cleanup.test.ts @@ -21,7 +21,7 @@ import { getFooType, getBarType, dummyModelVersion, -} from './base.fixtures'; +} from '../fixtures/zdt_base.fixtures'; export const logFilePath = Path.join(__dirname, 'document_cleanup.test.log'); diff --git a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/jest.integration.config.js b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/jest.integration.config.js similarity index 96% rename from src/core/server/integration_tests/saved_objects/migrations/zero_downtime/jest.integration.config.js rename to src/core/server/integration_tests/saved_objects/migrations/zdt_1/jest.integration.config.js index 4772e43faa148..04b7ddfc4377a 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/jest.integration.config.js +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/jest.integration.config.js @@ -13,7 +13,7 @@ module.exports = { // see https://github.com/elastic/kibana/pull/130255/ preset: '@kbn/test/jest_integration', rootDir: '../../../../../../..', - roots: ['/src/core/server/integration_tests/saved_objects/migrations/zero_downtime'], + roots: ['/src/core/server/integration_tests/saved_objects/migrations/zdt_1'], // must override to match all test given there is no `integration_tests` subfolder testMatch: ['**/*.test.{js,mjs,ts,tsx}'], }; diff --git a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/mapping_version_conflict.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/mapping_version_conflict.test.ts similarity index 95% rename from src/core/server/integration_tests/saved_objects/migrations/zero_downtime/mapping_version_conflict.test.ts rename to src/core/server/integration_tests/saved_objects/migrations/zdt_1/mapping_version_conflict.test.ts index 1674bf747f7b1..50edd85babe8e 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/mapping_version_conflict.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/mapping_version_conflict.test.ts @@ -12,7 +12,12 @@ import { createTestServers, type TestElasticsearchUtils } from '@kbn/core-test-h import '../jest_matchers'; import { getKibanaMigratorTestKit } from '../kibana_migrator_test_kit'; import { delay, parseLogFile } from '../test_utils'; -import { getBaseMigratorParams, getFooType, getBarType, dummyModelVersion } from './base.fixtures'; +import { + getBaseMigratorParams, + getFooType, + getBarType, + dummyModelVersion, +} from '../fixtures/zdt_base.fixtures'; export const logFilePath = Path.join(__dirname, 'mapping_version_conflict.test.log'); @@ -106,8 +111,8 @@ describe('ZDT upgrades - mapping model version conflict', () => { expect(aliases).toEqual(['.kibana', '.kibana_8.8.0']); expect(mappingMeta.mappingVersions).toEqual({ - foo: 2, - bar: 2, + foo: '10.2.0', + bar: '10.2.0', }); const records = await parseLogFile(logFilePath); diff --git a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/rerun_same_version.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/rerun_same_version.test.ts similarity index 98% rename from src/core/server/integration_tests/saved_objects/migrations/zero_downtime/rerun_same_version.test.ts rename to src/core/server/integration_tests/saved_objects/migrations/zdt_1/rerun_same_version.test.ts index 2be7c93965693..42ac41a84c92c 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/rerun_same_version.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/rerun_same_version.test.ts @@ -12,7 +12,7 @@ import { createTestServers, type TestElasticsearchUtils } from '@kbn/core-test-h import '../jest_matchers'; import { getKibanaMigratorTestKit } from '../kibana_migrator_test_kit'; import { delay, parseLogFile } from '../test_utils'; -import { getBaseMigratorParams, getFooType, getBarType } from './base.fixtures'; +import { getBaseMigratorParams, getFooType, getBarType } from '../fixtures/zdt_base.fixtures'; export const logFilePath = Path.join(__dirname, 'rerun_same_version.test.log'); diff --git a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/standard_workflow.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/standard_workflow.test.ts similarity index 99% rename from src/core/server/integration_tests/saved_objects/migrations/zero_downtime/standard_workflow.test.ts rename to src/core/server/integration_tests/saved_objects/migrations/zdt_1/standard_workflow.test.ts index 2fbbe7e090267..2e08df454f111 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/standard_workflow.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/standard_workflow.test.ts @@ -19,7 +19,7 @@ import { getSampleAType, getSampleBType, dummyModelVersion, -} from './base.fixtures'; +} from '../fixtures/zdt_base.fixtures'; export const logFilePath = Path.join(__dirname, 'standard_workflow.test.log'); diff --git a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/update_mappings.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/update_mappings.test.ts similarity index 95% rename from src/core/server/integration_tests/saved_objects/migrations/zero_downtime/update_mappings.test.ts rename to src/core/server/integration_tests/saved_objects/migrations/zdt_1/update_mappings.test.ts index 9829da66a965e..f8f49147ef753 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/zero_downtime/update_mappings.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_1/update_mappings.test.ts @@ -12,7 +12,12 @@ import { createTestServers, type TestElasticsearchUtils } from '@kbn/core-test-h import '../jest_matchers'; import { getKibanaMigratorTestKit } from '../kibana_migrator_test_kit'; import { delay, parseLogFile } from '../test_utils'; -import { getBaseMigratorParams, getFooType, getBarType, dummyModelVersion } from './base.fixtures'; +import { + getBaseMigratorParams, + getFooType, + getBarType, + dummyModelVersion, +} from '../fixtures/zdt_base.fixtures'; export const logFilePath = Path.join(__dirname, 'update_mappings.test.log'); @@ -111,8 +116,8 @@ describe('ZDT upgrades - basic mapping update', () => { ); expect(mappingMeta.mappingVersions).toEqual({ - foo: 3, - bar: 2, + foo: '10.3.0', + bar: '10.2.0', }); const records = await parseLogFile(logFilePath); diff --git a/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/basic_document_migration.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/basic_document_migration.test.ts new file mode 100644 index 0000000000000..14d707e51a7ec --- /dev/null +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/basic_document_migration.test.ts @@ -0,0 +1,243 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Path from 'path'; +import fs from 'fs/promises'; +import { range, sortBy } from 'lodash'; +import { createTestServers, type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; +import { SavedObjectsBulkCreateObject } from '@kbn/core-saved-objects-api-server'; +import '../jest_matchers'; +import { getKibanaMigratorTestKit } from '../kibana_migrator_test_kit'; +import { delay, parseLogFile } from '../test_utils'; +import { + getBaseMigratorParams, + getSampleAType, + getLegacyType, +} from '../fixtures/zdt_base.fixtures'; + +const logFilePath = Path.join(__dirname, 'basic_document_migration.test.log'); + +describe('ZDT with v2 compat - basic document migration', () => { + let esServer: TestElasticsearchUtils['es']; + + const startElasticsearch = async () => { + const { startES } = createTestServers({ + adjustTimeout: (t: number) => jest.setTimeout(t), + settings: { + es: { + license: 'basic', + }, + }, + }); + return await startES(); + }; + + beforeAll(async () => { + await fs.unlink(logFilePath).catch(() => {}); + esServer = await startElasticsearch(); + }); + + afterAll(async () => { + await esServer?.stop(); + await delay(10); + }); + + const createBaseline = async () => { + const { runMigrations, savedObjectsRepository } = await getKibanaMigratorTestKit({ + ...getBaseMigratorParams(), + types: [getSampleAType(), getLegacyType()], + }); + await runMigrations(); + + const sampleAObjs = range(5).map((number) => ({ + id: `a-${number}`, + type: 'sample_a', + attributes: { + keyword: `a_${number}`, + boolean: true, + }, + })); + + await savedObjectsRepository.bulkCreate(sampleAObjs); + + const legacyTypeObjs = range(5).map((number) => ({ + id: `legacy-${number}`, + type: 'legacy', + attributes: { + someField: `legacy ${number}`, + }, + })); + + await savedObjectsRepository.bulkCreate(legacyTypeObjs); + }; + + it('migrates the documents', async () => { + await createBaseline(); + + const typeA = getSampleAType(); + const legacyType = getLegacyType(); + + // typeA -> we add a new field and bump the model version by one with a migration + + typeA.mappings.properties = { + ...typeA.mappings.properties, + someAddedField: { type: 'keyword' }, + }; + + typeA.modelVersions = { + ...typeA.modelVersions, + '2': { + modelChange: { + type: 'expansion', + transformation: { + up: (doc) => { + return { + document: { + ...doc, + attributes: { + ...doc.attributes, + someAddedField: `${doc.attributes.keyword}-mig`, + }, + }, + }; + }, + down: jest.fn(), + }, + addedMappings: { + someAddedField: { type: 'keyword' }, + }, + }, + }, + }; + + // legacyType -> we add a new migration + + legacyType.mappings.properties = { + ...legacyType.mappings.properties, + newField: { type: 'text' }, + }; + + legacyType.migrations = { + ...legacyType.migrations, + '8.0.0': (document) => { + return { + ...document, + attributes: { + ...document.attributes, + newField: `populated ${document.id}`, + }, + }; + }, + }; + + const { runMigrations, client, savedObjectsRepository } = await getKibanaMigratorTestKit({ + ...getBaseMigratorParams(), + logFilePath, + types: [typeA, legacyType], + }); + + await runMigrations(); + + const indices = await client.indices.get({ index: '.kibana*' }); + expect(Object.keys(indices)).toEqual(['.kibana_1']); + + const index = indices['.kibana_1']; + const mappings = index.mappings ?? {}; + const mappingMeta = mappings._meta ?? {}; + + expect(mappings.properties).toEqual( + expect.objectContaining({ + sample_a: typeA.mappings, + legacy: legacyType.mappings, + }) + ); + + expect(mappingMeta.docVersions).toEqual({ + sample_a: '10.2.0', + legacy: '8.0.0', + }); + + const { saved_objects: sampleADocs } = await savedObjectsRepository.find({ type: 'sample_a' }); + const { saved_objects: legacyDocs } = await savedObjectsRepository.find({ type: 'legacy' }); + + expect(sampleADocs).toHaveLength(5); + expect(legacyDocs).toHaveLength(5); + + const sampleAData = sortBy(sampleADocs, 'id').map((object) => ({ + id: object.id, + type: object.type, + attributes: object.attributes, + })); + + expect(sampleAData).toEqual([ + { + id: 'a-0', + type: 'sample_a', + attributes: { boolean: true, keyword: 'a_0', someAddedField: 'a_0-mig' }, + }, + { + id: 'a-1', + type: 'sample_a', + attributes: { boolean: true, keyword: 'a_1', someAddedField: 'a_1-mig' }, + }, + { + id: 'a-2', + type: 'sample_a', + attributes: { boolean: true, keyword: 'a_2', someAddedField: 'a_2-mig' }, + }, + { + id: 'a-3', + type: 'sample_a', + attributes: { boolean: true, keyword: 'a_3', someAddedField: 'a_3-mig' }, + }, + { + id: 'a-4', + type: 'sample_a', + attributes: { boolean: true, keyword: 'a_4', someAddedField: 'a_4-mig' }, + }, + ]); + + const sampleBData = sortBy(legacyDocs, 'id').map((object) => ({ + id: object.id, + type: object.type, + attributes: object.attributes, + })); + + expect(sampleBData).toEqual([ + { + id: 'legacy-0', + type: 'legacy', + attributes: { someField: `legacy 0`, newField: `populated legacy-0` }, + }, + { + id: 'legacy-1', + type: 'legacy', + attributes: { someField: `legacy 1`, newField: `populated legacy-1` }, + }, + { + id: 'legacy-2', + type: 'legacy', + attributes: { someField: `legacy 2`, newField: `populated legacy-2` }, + }, + { + id: 'legacy-3', + type: 'legacy', + attributes: { someField: `legacy 3`, newField: `populated legacy-3` }, + }, + { + id: 'legacy-4', + type: 'legacy', + attributes: { someField: `legacy 4`, newField: `populated legacy-4` }, + }, + ]); + + const records = await parseLogFile(logFilePath); + expect(records).toContainLogEntry('Starting to process 10 documents'); + expect(records).toContainLogEntry('Migration completed'); + }); +}); diff --git a/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/create_index.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/create_index.test.ts new file mode 100644 index 0000000000000..4933c5b712c21 --- /dev/null +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/create_index.test.ts @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Path from 'path'; +import fs from 'fs/promises'; +import '../jest_matchers'; +import { createTestServers, type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; +import { getKibanaMigratorTestKit } from '../kibana_migrator_test_kit'; +import { delay, parseLogFile } from '../test_utils'; +import { getBaseMigratorParams, getFooType, getLegacyType } from '../fixtures/zdt_base.fixtures'; + +const logFilePath = Path.join(__dirname, 'create_index.test.log'); + +describe('ZDT with v2 compat - running on a fresh cluster', () => { + let esServer: TestElasticsearchUtils['es']; + + const startElasticsearch = async () => { + const { startES } = createTestServers({ + adjustTimeout: (t: number) => jest.setTimeout(t), + settings: { + es: { + license: 'basic', + }, + }, + }); + return await startES(); + }; + + beforeAll(async () => { + await fs.unlink(logFilePath).catch(() => {}); + esServer = await startElasticsearch(); + }); + + afterAll(async () => { + await esServer?.stop(); + await delay(10); + }); + + it('create the index with the correct mappings and meta', async () => { + const fooType = getFooType(); + const legacyType = getLegacyType(); + + const { runMigrations, client } = await getKibanaMigratorTestKit({ + ...getBaseMigratorParams(), + logFilePath, + types: [fooType, legacyType], + }); + + const result = await runMigrations(); + + expect(result).toEqual([ + { + destIndex: '.kibana', + elapsedMs: expect.any(Number), + status: 'patched', + }, + ]); + + const indices = await client.indices.get({ index: '.kibana*' }); + + expect(Object.keys(indices)).toEqual(['.kibana_1']); + + const index = indices['.kibana_1']; + const aliases = Object.keys(index.aliases ?? {}).sort(); + const mappings = index.mappings ?? {}; + const mappingMeta = mappings._meta ?? {}; + + expect(aliases).toEqual(['.kibana', '.kibana_8.8.0']); + + expect(mappings.properties).toEqual( + expect.objectContaining({ + foo: fooType.mappings, + legacy: legacyType.mappings, + }) + ); + + expect(mappingMeta).toEqual({ + docVersions: { + foo: '10.2.0', + legacy: '7.5.0', + }, + mappingVersions: { + foo: '10.2.0', + legacy: '7.5.0', + }, + migrationState: expect.objectContaining({ + convertingDocuments: false, + }), + }); + + const records = await parseLogFile(logFilePath); + + expect(records).toContainLogEntry('INIT -> CREATE_TARGET_INDEX'); + expect(records).toContainLogEntry('CREATE_TARGET_INDEX -> UPDATE_ALIASES'); + expect(records).toContainLogEntry('UPDATE_ALIASES -> INDEX_STATE_UPDATE_DONE'); + expect(records).toContainLogEntry('INDEX_STATE_UPDATE_DONE -> DONE'); + expect(records).toContainLogEntry('Migration completed'); + }); +}); diff --git a/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/jest.integration.config.js b/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/jest.integration.config.js new file mode 100644 index 0000000000000..33c212f19a68e --- /dev/null +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/jest.integration.config.js @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + // TODO replace the line below with + // preset: '@kbn/test/jest_integration_node + // to do so, we must fix all integration tests first + // see https://github.com/elastic/kibana/pull/130255/ + preset: '@kbn/test/jest_integration', + rootDir: '../../../../../../..', + roots: ['/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat'], + // must override to match all test given there is no `integration_tests` subfolder + testMatch: ['**/*.test.{js,mjs,ts,tsx}'], +}; diff --git a/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/switch_to_model_version.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/switch_to_model_version.test.ts new file mode 100644 index 0000000000000..60aa99da52e72 --- /dev/null +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/switch_to_model_version.test.ts @@ -0,0 +1,182 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Path from 'path'; +import fs from 'fs/promises'; +import { range, sortBy } from 'lodash'; +import { createTestServers, type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; +import { SavedObjectsBulkCreateObject } from '@kbn/core-saved-objects-api-server'; +import '../jest_matchers'; +import { getKibanaMigratorTestKit } from '../kibana_migrator_test_kit'; +import { delay, parseLogFile, createType } from '../test_utils'; +import { getBaseMigratorParams, noopMigration } from '../fixtures/zdt_base.fixtures'; + +const logFilePath = Path.join(__dirname, 'switch_to_model_version.test.log'); + +describe('ZDT with v2 compat - type switching from migration to model version', () => { + let esServer: TestElasticsearchUtils['es']; + + const startElasticsearch = async () => { + const { startES } = createTestServers({ + adjustTimeout: (t: number) => jest.setTimeout(t), + settings: { + es: { + license: 'basic', + }, + }, + }); + return await startES(); + }; + + beforeAll(async () => { + await fs.unlink(logFilePath).catch(() => {}); + esServer = await startElasticsearch(); + }); + + afterAll(async () => { + await esServer?.stop(); + await delay(10); + }); + + const typeBefore = createType({ + name: 'switching_type', + mappings: { + properties: { + text: { type: 'text' }, + keyword: { type: 'keyword' }, + }, + }, + migrations: { + '7.0.0': noopMigration, + '7.5.0': noopMigration, + }, + }); + + const typeAfter = createType({ + name: 'switching_type', + mappings: { + properties: { + text: { type: 'text' }, + keyword: { type: 'keyword' }, + newField1: { type: 'text' }, + newField2: { type: 'text' }, + }, + }, + migrations: { + '7.0.0': noopMigration, + '7.5.0': noopMigration, + '7.9.0': (doc) => { + return { + ...doc, + attributes: { + ...doc.attributes, + newField1: `new1 ${doc.id}`, + }, + }; + }, + }, + switchToModelVersionAt: '8.0.0', + modelVersions: { + 1: { + modelChange: { + type: 'expansion', + transformation: { + up: (doc) => { + return { + document: { + ...doc, + attributes: { + ...doc.attributes, + newField2: `new2 ${doc.id}`, + }, + }, + }; + }, + down: jest.fn(), + }, + }, + }, + }, + }); + + const createBaseline = async () => { + const { runMigrations, savedObjectsRepository } = await getKibanaMigratorTestKit({ + ...getBaseMigratorParams(), + types: [typeBefore], + }); + await runMigrations(); + + const sampleObjs = range(5).map((number) => ({ + id: `doc-${number}`, + type: 'switching_type', + attributes: { + text: `text ${number}`, + keyword: `kw ${number}`, + }, + })); + + await savedObjectsRepository.bulkCreate(sampleObjs); + }; + + it('migrates the documents', async () => { + await createBaseline(); + + const { runMigrations, client, savedObjectsRepository } = await getKibanaMigratorTestKit({ + ...getBaseMigratorParams(), + logFilePath, + types: [typeAfter], + }); + + await runMigrations(); + + const indices = await client.indices.get({ index: '.kibana*' }); + expect(Object.keys(indices)).toEqual(['.kibana_1']); + + const index = indices['.kibana_1']; + const mappings = index.mappings ?? {}; + const mappingMeta = mappings._meta ?? {}; + + expect(mappings.properties).toEqual( + expect.objectContaining({ + switching_type: typeAfter.mappings, + }) + ); + + expect(mappingMeta.docVersions).toEqual({ + switching_type: '10.1.0', + }); + + const { saved_objects: sampleDocs } = await savedObjectsRepository.find({ + type: 'switching_type', + }); + + expect(sampleDocs).toHaveLength(5); + + const sampleData = sortBy(sampleDocs, 'id').map((object) => ({ + id: object.id, + type: object.type, + attributes: object.attributes, + })); + + expect(sampleData).toEqual( + range(5).map((i) => ({ + id: `doc-${i}`, + type: 'switching_type', + attributes: { + text: `text ${i}`, + keyword: `kw ${i}`, + newField1: `new1 doc-${i}`, + newField2: `new2 doc-${i}`, + }, + })) + ); + + const records = await parseLogFile(logFilePath); + expect(records).toContainLogEntry('Migration completed'); + }); +}); diff --git a/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/update_mappings.test.ts b/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/update_mappings.test.ts new file mode 100644 index 0000000000000..ea25e6549c189 --- /dev/null +++ b/src/core/server/integration_tests/saved_objects/migrations/zdt_v2_compat/update_mappings.test.ts @@ -0,0 +1,136 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Path from 'path'; +import fs from 'fs/promises'; +import { createTestServers, type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; +import '../jest_matchers'; +import { getKibanaMigratorTestKit } from '../kibana_migrator_test_kit'; +import { delay, parseLogFile } from '../test_utils'; +import { + getBaseMigratorParams, + getFooType, + getLegacyType, + dummyModelVersion, + noopMigration, +} from '../fixtures/zdt_base.fixtures'; + +export const logFilePath = Path.join(__dirname, 'update_mappings.test.log'); + +describe('ZDT with v2 compat - basic mapping update', () => { + let esServer: TestElasticsearchUtils['es']; + + const startElasticsearch = async () => { + const { startES } = createTestServers({ + adjustTimeout: (t: number) => jest.setTimeout(t), + settings: { + es: { + license: 'basic', + }, + }, + }); + return await startES(); + }; + + beforeAll(async () => { + await fs.unlink(logFilePath).catch(() => {}); + esServer = await startElasticsearch(); + }); + + afterAll(async () => { + await esServer?.stop(); + await delay(10); + }); + + const createBaseline = async () => { + const fooType = getFooType(); + const legacyType = getLegacyType(); + const { runMigrations } = await getKibanaMigratorTestKit({ + ...getBaseMigratorParams(), + types: [fooType, legacyType], + }); + await runMigrations(); + }; + + it('updates the mappings and the meta', async () => { + await createBaseline(); + + const fooType = getFooType(); + const legacyType = getLegacyType(); + + // increasing the model version of the types + fooType.modelVersions = { + ...fooType.modelVersions, + '3': dummyModelVersion, + }; + fooType.mappings.properties = { + ...fooType.mappings.properties, + someAddedField: { type: 'keyword' }, + }; + + legacyType.migrations = { + ...legacyType.migrations, + '8.0.0': noopMigration, + }; + legacyType.mappings.properties = { + ...legacyType.mappings.properties, + anotherAddedField: { type: 'boolean' }, + }; + + const { runMigrations, client } = await getKibanaMigratorTestKit({ + ...getBaseMigratorParams(), + logFilePath, + types: [fooType, legacyType], + }); + + const result = await runMigrations(); + + expect(result).toEqual([ + { + destIndex: '.kibana', + elapsedMs: expect.any(Number), + status: 'patched', + }, + ]); + + const indices = await client.indices.get({ index: '.kibana*' }); + + expect(Object.keys(indices)).toEqual(['.kibana_1']); + + const index = indices['.kibana_1']; + const aliases = Object.keys(index.aliases ?? {}).sort(); + const mappings = index.mappings ?? {}; + const mappingMeta = mappings._meta ?? {}; + + expect(aliases).toEqual(['.kibana', '.kibana_8.8.0']); + + expect(mappings.properties).toEqual( + expect.objectContaining({ + foo: fooType.mappings, + legacy: legacyType.mappings, + }) + ); + + expect(mappingMeta.mappingVersions).toEqual({ + foo: '10.3.0', + legacy: '8.0.0', + }); + + const records = await parseLogFile(logFilePath); + + expect(records).toContainLogEntry('INIT -> UPDATE_INDEX_MAPPINGS'); + expect(records).toContainLogEntry( + 'UPDATE_INDEX_MAPPINGS -> UPDATE_INDEX_MAPPINGS_WAIT_FOR_TASK' + ); + expect(records).toContainLogEntry( + 'UPDATE_INDEX_MAPPINGS_WAIT_FOR_TASK -> UPDATE_MAPPING_MODEL_VERSIONS' + ); + expect(records).toContainLogEntry('UPDATE_MAPPING_MODEL_VERSIONS -> INDEX_STATE_UPDATE_DONE'); + expect(records).toContainLogEntry('Migration completed'); + }); +}); diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker index 3123aa7f391df..7eaae8b09629f 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker @@ -413,6 +413,7 @@ kibana_vars=( xpack.task_manager.version_conflict_threshold xpack.task_manager.event_loop_delay.monitor xpack.task_manager.event_loop_delay.warn_threshold + xpack.task_manager.worker_utilization_running_average_window xpack.uptime.index serverless ) diff --git a/src/plugins/management/public/application.tsx b/src/plugins/management/public/application.tsx index dc16faa4f6d04..8c4a8af510391 100644 --- a/src/plugins/management/public/application.tsx +++ b/src/plugins/management/public/application.tsx @@ -16,15 +16,6 @@ export const renderApp = async ( { history, appBasePath, element, theme$ }: AppMountParameters, dependencies: ManagementAppDependencies ) => { - ReactDOM.render( - , - element - ); - + ReactDOM.render(, element); return () => ReactDOM.unmountComponentAtNode(element); }; diff --git a/src/plugins/management/public/components/management_app/management_app.tsx b/src/plugins/management/public/components/management_app/management_app.tsx index bc0b88e7dffcb..50ab45a867c98 100644 --- a/src/plugins/management/public/components/management_app/management_app.tsx +++ b/src/plugins/management/public/components/management_app/management_app.tsx @@ -8,12 +8,15 @@ import './management_app.scss'; import React, { useState, useEffect, useCallback } from 'react'; +import { BehaviorSubject } from 'rxjs'; + import { I18nProvider } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { AppMountParameters, ChromeBreadcrumb, ScopedHistory } from '@kbn/core/public'; import { reactRouterNavigate, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { KibanaPageTemplate, KibanaPageTemplateProps } from '@kbn/shared-ux-page-kibana-template'; +import useObservable from 'react-use/lib/useObservable'; import { ManagementSection, MANAGEMENT_BREADCRUMB, @@ -34,12 +37,14 @@ export interface ManagementAppDependencies { sections: SectionsServiceStart; kibanaVersion: string; setBreadcrumbs: (newBreadcrumbs: ChromeBreadcrumb[]) => void; + isSidebarEnabled$: BehaviorSubject; } export const ManagementApp = ({ dependencies, history, theme$ }: ManagementAppProps) => { - const { setBreadcrumbs } = dependencies; + const { setBreadcrumbs, isSidebarEnabled$ } = dependencies; const [selectedId, setSelectedId] = useState(''); const [sections, setSections] = useState(); + const isSidebarEnabled = useObservable(isSidebarEnabled$); const onAppMounted = useCallback((id: string) => { setSelectedId(id); @@ -75,18 +80,20 @@ export const ManagementApp = ({ dependencies, history, theme$ }: ManagementAppPr return null; } - const solution: KibanaPageTemplateProps['solutionNav'] = { - name: i18n.translate('management.nav.label', { - defaultMessage: 'Management', - }), - icon: 'managementApp', - 'data-test-subj': 'mgtSideBarNav', - items: managementSidebarNav({ - selectedId, - sections, - history, - }), - }; + const solution: KibanaPageTemplateProps['solutionNav'] | undefined = isSidebarEnabled + ? { + name: i18n.translate('management.nav.label', { + defaultMessage: 'Management', + }), + icon: 'managementApp', + 'data-test-subj': 'mgtSideBarNav', + items: managementSidebarNav({ + selectedId, + sections, + history, + }), + } + : undefined; return ( diff --git a/src/plugins/management/public/mocks/index.ts b/src/plugins/management/public/mocks/index.ts index 92f6f9e1ed4ce..ec4ec2fa7883a 100644 --- a/src/plugins/management/public/mocks/index.ts +++ b/src/plugins/management/public/mocks/index.ts @@ -42,7 +42,7 @@ const createSetupContract = (): ManagementSetup => ({ }); const createStartContract = (): ManagementStart => ({ - sections: {}, + setIsSidebarEnabled: jest.fn(), }); export const managementPluginMock = { diff --git a/src/plugins/management/public/plugin.ts b/src/plugins/management/public/plugin.ts index 0cdb83d4b6793..8310074a63159 100644 --- a/src/plugins/management/public/plugin.ts +++ b/src/plugins/management/public/plugin.ts @@ -71,11 +71,14 @@ export class ManagementPlugin private hasAnyEnabledApps = true; + private isSidebarEnabled$ = new BehaviorSubject(true); + constructor(private initializerContext: PluginInitializerContext) {} public setup(core: CoreSetup, { home, share }: ManagementSetupDependencies) { const kibanaVersion = this.initializerContext.env.packageInfo.version; const locator = share.url.locators.create(new ManagementAppLocatorDefinition()); + const managementPlugin = this; if (home) { home.featureCatalogue.register({ @@ -111,6 +114,7 @@ export class ManagementPlugin sections: getSectionsServiceStartPrivate(), kibanaVersion, setBreadcrumbs: coreStart.chrome.setBreadcrumbs, + isSidebarEnabled$: managementPlugin.isSidebarEnabled$, }); }, }); @@ -121,7 +125,7 @@ export class ManagementPlugin }; } - public start(core: CoreStart, plugins: ManagementStartDependencies) { + public start(core: CoreStart, _plugins: ManagementStartDependencies): ManagementStart { this.managementSections.start({ capabilities: core.application.capabilities }); this.hasAnyEnabledApps = getSectionsServiceStartPrivate() .getSectionsEnabled() @@ -136,6 +140,9 @@ export class ManagementPlugin }); } - return {}; + return { + setIsSidebarEnabled: (isSidebarEnabled: boolean) => + this.isSidebarEnabled$.next(isSidebarEnabled), + }; } } diff --git a/src/plugins/management/public/types.ts b/src/plugins/management/public/types.ts index 5d8d963ea981e..0f632b818a950 100644 --- a/src/plugins/management/public/types.ts +++ b/src/plugins/management/public/types.ts @@ -27,8 +27,9 @@ export interface DefinedSections { stack: ManagementSection; } -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface ManagementStart {} +export interface ManagementStart { + setIsSidebarEnabled: (enabled: boolean) => void; +} export interface ManagementSectionsStartPrivate { getSectionsEnabled: () => ManagementSection[]; diff --git a/tsconfig.base.json b/tsconfig.base.json index 58eb34d40debd..01e7276e6bb86 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -912,6 +912,8 @@ "@kbn/maps-plugin/*": ["x-pack/plugins/maps/*"], "@kbn/ml-agg-utils": ["x-pack/packages/ml/agg_utils"], "@kbn/ml-agg-utils/*": ["x-pack/packages/ml/agg_utils/*"], + "@kbn/ml-anomaly-utils": ["x-pack/packages/ml/anomaly_utils"], + "@kbn/ml-anomaly-utils/*": ["x-pack/packages/ml/anomaly_utils/*"], "@kbn/ml-date-picker": ["x-pack/packages/ml/date_picker"], "@kbn/ml-date-picker/*": ["x-pack/packages/ml/date_picker/*"], "@kbn/ml-error-utils": ["x-pack/packages/ml/error_utils"], @@ -1150,6 +1152,8 @@ "@kbn/serverless-project-switcher/*": ["packages/serverless/project_switcher/*"], "@kbn/serverless-search": ["x-pack/plugins/serverless_search"], "@kbn/serverless-search/*": ["x-pack/plugins/serverless_search/*"], + "@kbn/serverless-security": ["x-pack/plugins/serverless_security"], + "@kbn/serverless-security/*": ["x-pack/plugins/serverless_security/*"], "@kbn/serverless-storybook-config": ["packages/serverless/storybook/config"], "@kbn/serverless-storybook-config/*": ["packages/serverless/storybook/config/*"], "@kbn/serverless-types": ["packages/serverless/types"], diff --git a/versions.json b/versions.json index 5b24835e23d97..1569a8e6dff4f 100644 --- a/versions.json +++ b/versions.json @@ -14,13 +14,13 @@ "previousMinor": true }, { - "version": "8.7.1", + "version": "8.7.2", "branch": "8.7", "currentMajor": true, "previousMinor": true }, { - "version": "7.17.10", + "version": "7.17.11", "branch": "7.17", "previousMajor": true } diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index 8e85011f366d8..59dea3cb2c9a3 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -2,6 +2,7 @@ "prefix": "xpack", "paths": { "xpack.actions": "plugins/actions", + "xpack.aiops": ["packages/ml/aiops_components", "plugins/aiops"], "xpack.alerting": "plugins/alerting", "xpack.eventLog": "plugins/event_log", "xpack.stackAlerts": "plugins/stack_alerts", @@ -45,8 +46,12 @@ "xpack.logstash": ["plugins/logstash"], "xpack.main": "legacy/plugins/xpack_main", "xpack.maps": ["plugins/maps"], - "xpack.aiops": ["packages/ml/aiops_components", "plugins/aiops"], - "xpack.ml": ["packages/ml/date_picker", "packages/ml/trained_models_utils", "plugins/ml"], + "xpack.ml": [ + "packages/ml/anomaly_utils", + "packages/ml/date_picker", + "packages/ml/trained_models_utils", + "plugins/ml" + ], "xpack.monitoring": ["plugins/monitoring"], "xpack.observability": "plugins/observability", "xpack.observabilityShared": "plugins/observability_shared", diff --git a/x-pack/packages/ml/anomaly_utils/README.md b/x-pack/packages/ml/anomaly_utils/README.md new file mode 100644 index 0000000000000..941771b80f7ef --- /dev/null +++ b/x-pack/packages/ml/anomaly_utils/README.md @@ -0,0 +1,3 @@ +# @kbn/ml-anomaly-utils + +Utility functions, constants and types for Machine Learning's Anomaly Detection. diff --git a/x-pack/packages/ml/anomaly_utils/anomaly_severity.ts b/x-pack/packages/ml/anomaly_utils/anomaly_severity.ts new file mode 100644 index 0000000000000..bd11286784042 --- /dev/null +++ b/x-pack/packages/ml/anomaly_utils/anomaly_severity.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. + */ + +/** + * Labels displayed in the ML UI to indicate the severity of the anomaly according + * to the normalized anomaly score. + */ +export enum ML_ANOMALY_SEVERITY { + /** + * Anomalies are displayed as critical severity when the score is greater than or equal to 75. + */ + CRITICAL = 'critical', + + /** + * Anomalies are displayed as major severity when the score is greater than or equal to 50 and less than 75. + */ + MAJOR = 'major', + + /** + * Anomalies are displayed as minor severity when the score is greater than or equal to 25 and less than 50. + */ + MINOR = 'minor', + + /** + * Anomalies are displayed as warning severity when the score is greater than or equal to 3 and less than 25. + * Note in some parts of the UI, warning severity is used when the score is greater than or equal to 0. + */ + WARNING = 'warning', + + /** + * Anomalies are displayed as low severity in some parts of the ML UI when the score is greater than or equal to 0 and less than 3. + */ + LOW = 'low', + + /** + * Anomalies are displayed as unknown severity if the anomaly score is not known. + */ + UNKNOWN = 'unknown', +} + +/** + * Interface for severity types to be used in ML_ANOMALY_SEVERITY_TYPES. + * + * @export + * @interface MlSeverityType + * @typedef {MlSeverityType} + */ +export interface MlSeverityType { + /** + * One of ML_ANOMALY_SEVERITY + * @type {ML_ANOMALY_SEVERITY} + */ + id: ML_ANOMALY_SEVERITY; + /** + * Translated ML_ANOMALY_SEVERITY + * @type {string} + */ + label: string; +} diff --git a/x-pack/packages/ml/anomaly_utils/anomaly_severity_types.ts b/x-pack/packages/ml/anomaly_utils/anomaly_severity_types.ts new file mode 100644 index 0000000000000..e3377896b6c99 --- /dev/null +++ b/x-pack/packages/ml/anomaly_utils/anomaly_severity_types.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { type MlSeverityType, ML_ANOMALY_SEVERITY } from './anomaly_severity'; + +export const ML_ANOMALY_SEVERITY_TYPES: Record = { + critical: { + id: ML_ANOMALY_SEVERITY.CRITICAL, + label: i18n.translate('xpack.ml.anomalyUtils.severity.criticalLabel', { + defaultMessage: 'critical', + }), + }, + major: { + id: ML_ANOMALY_SEVERITY.MAJOR, + label: i18n.translate('xpack.ml.anomalyUtils.severity.majorLabel', { + defaultMessage: 'major', + }), + }, + minor: { + id: ML_ANOMALY_SEVERITY.MINOR, + label: i18n.translate('xpack.ml.anomalyUtils.severity.minorLabel', { + defaultMessage: 'minor', + }), + }, + warning: { + id: ML_ANOMALY_SEVERITY.WARNING, + label: i18n.translate('xpack.ml.anomalyUtils.severity.warningLabel', { + defaultMessage: 'warning', + }), + }, + unknown: { + id: ML_ANOMALY_SEVERITY.UNKNOWN, + label: i18n.translate('xpack.ml.anomalyUtils.severity.unknownLabel', { + defaultMessage: 'unknown', + }), + }, + low: { + id: ML_ANOMALY_SEVERITY.LOW, + label: i18n.translate('xpack.ml.anomalyUtils.severityWithLow.lowLabel', { + defaultMessage: 'low', + }), + }, +}; diff --git a/x-pack/packages/ml/anomaly_utils/anomaly_threshold.ts b/x-pack/packages/ml/anomaly_utils/anomaly_threshold.ts new file mode 100644 index 0000000000000..aed964be280de --- /dev/null +++ b/x-pack/packages/ml/anomaly_utils/anomaly_threshold.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. + */ + +/** + * Anomaly score numeric thresholds to indicate the severity of the anomaly. + */ +export enum ML_ANOMALY_THRESHOLD { + /** + * Threshold at which anomalies are labelled in the UI as critical. + */ + CRITICAL = 75, + + /** + * Threshold at which anomalies are labelled in the UI as major. + */ + MAJOR = 50, + + /** + * Threshold at which anomalies are labelled in the UI as minor. + */ + MINOR = 25, + + /** + * Threshold at which anomalies are labelled in the UI as warning. + */ + WARNING = 3, + + /** + * Threshold at which anomalies are labelled in the UI as low. + */ + LOW = 0, +} diff --git a/x-pack/plugins/ml/common/util/anomaly_utils.test.ts b/x-pack/packages/ml/anomaly_utils/anomaly_utils.test.ts similarity index 84% rename from x-pack/plugins/ml/common/util/anomaly_utils.test.ts rename to x-pack/packages/ml/anomaly_utils/anomaly_utils.test.ts index b707a61d5b9bc..9b2f25982b00a 100644 --- a/x-pack/plugins/ml/common/util/anomaly_utils.test.ts +++ b/x-pack/packages/ml/anomaly_utils/anomaly_utils.test.ts @@ -5,16 +5,14 @@ * 2.0. */ -import { AnomalyRecordDoc } from '../types/anomalies'; +import type { MlAnomalyRecordDoc } from './types'; import { aggregationTypeTransform, getEntityFieldList, getEntityFieldName, getEntityFieldValue, - getSeverity, getSeverityWithLow, - getSeverityColor, isRuleSupported, showActualForFunction, showTypicalForFunction, @@ -22,7 +20,7 @@ import { } from './anomaly_utils'; describe('ML - anomaly utils', () => { - const partitionEntityRecord: AnomalyRecordDoc = { + const partitionEntityRecord: MlAnomalyRecordDoc = { job_id: 'farequote', result_type: 'record', probability: 0.012818, @@ -39,7 +37,7 @@ describe('ML - anomaly utils', () => { field_name: 'responsetime', }; - const byEntityRecord: AnomalyRecordDoc = { + const byEntityRecord: MlAnomalyRecordDoc = { job_id: 'farequote', result_type: 'record', probability: 0.012818, @@ -56,7 +54,7 @@ describe('ML - anomaly utils', () => { field_name: 'responsetime', }; - const overEntityRecord: AnomalyRecordDoc = { + const overEntityRecord: MlAnomalyRecordDoc = { job_id: 'gallery', result_type: 'record', probability: 2.81806e-9, @@ -74,7 +72,7 @@ describe('ML - anomaly utils', () => { over_field_value: '37.157.32.164', }; - const noEntityRecord: AnomalyRecordDoc = { + const noEntityRecord: MlAnomalyRecordDoc = { job_id: 'farequote_no_by', result_type: 'record', probability: 0.0191711, @@ -89,7 +87,7 @@ describe('ML - anomaly utils', () => { field_name: 'responsetime', }; - const metricNoEntityRecord: AnomalyRecordDoc = { + const metricNoEntityRecord: MlAnomalyRecordDoc = { job_id: 'farequote_metric', result_type: 'record', probability: 0.030133495093182184, @@ -113,7 +111,7 @@ describe('ML - anomaly utils', () => { airline: ['NKS'], }; - const rareEntityRecord: AnomalyRecordDoc = { + const rareEntityRecord: MlAnomalyRecordDoc = { job_id: 'gallery', result_type: 'record', probability: 0.02277014211908481, @@ -166,34 +164,6 @@ describe('ML - anomaly utils', () => { status: ['206'], }; - describe('getSeverity', () => { - test('returns warning for 0 <= score < 25', () => { - expect(getSeverity(0).id).toBe('warning'); - expect(getSeverity(0.001).id).toBe('warning'); - expect(getSeverity(24.99).id).toBe('warning'); - }); - - test('returns minor for 25 <= score < 50', () => { - expect(getSeverity(25).id).toBe('minor'); - expect(getSeverity(49.99).id).toBe('minor'); - }); - - test('returns minor for 50 <= score < 75', () => { - expect(getSeverity(50).id).toBe('major'); - expect(getSeverity(74.99).id).toBe('major'); - }); - - test('returns critical for score >= 75', () => { - expect(getSeverity(75).id).toBe('critical'); - expect(getSeverity(100).id).toBe('critical'); - expect(getSeverity(1000).id).toBe('critical'); - }); - - test('returns unknown for scores less than 0', () => { - expect(getSeverity(-10).id).toBe('unknown'); - }); - }); - describe('getSeverityWithLow', () => { test('returns low for 0 <= score < 3', () => { expect(getSeverityWithLow(0).id).toBe('low'); @@ -227,41 +197,8 @@ describe('ML - anomaly utils', () => { }); }); - describe('getSeverityColor', () => { - test('returns correct hex code for low for 0 <= score < 3', () => { - expect(getSeverityColor(0)).toBe('#d2e9f7'); - expect(getSeverityColor(0.001)).toBe('#d2e9f7'); - expect(getSeverityColor(2.99)).toBe('#d2e9f7'); - }); - - test('returns correct hex code for warning for 3 <= score < 25', () => { - expect(getSeverityColor(3)).toBe('#8bc8fb'); - expect(getSeverityColor(24.99)).toBe('#8bc8fb'); - }); - - test('returns correct hex code for minor for 25 <= score < 50', () => { - expect(getSeverityColor(25)).toBe('#fdec25'); - expect(getSeverityColor(49.99)).toBe('#fdec25'); - }); - - test('returns correct hex code for major for 50 <= score < 75', () => { - expect(getSeverityColor(50)).toBe('#fba740'); - expect(getSeverityColor(74.99)).toBe('#fba740'); - }); - - test('returns correct hex code for critical for score >= 75', () => { - expect(getSeverityColor(75)).toBe('#fe5050'); - expect(getSeverityColor(100)).toBe('#fe5050'); - expect(getSeverityColor(1000)).toBe('#fe5050'); - }); - - test('returns correct hex code for unknown for scores less than 0', () => { - expect(getSeverityColor(-10)).toBe('#ffffff'); - }); - }); - describe('isMultiBucketAnomaly', () => { - const singleBucketAnomaly: AnomalyRecordDoc = { + const singleBucketAnomaly: MlAnomalyRecordDoc = { job_id: 'farequote_sb', result_type: 'record', probability: 0.0191711, @@ -283,7 +220,7 @@ describe('ML - anomaly utils', () => { }, }; - const multiBucketAnomaly: AnomalyRecordDoc = { + const multiBucketAnomaly: MlAnomalyRecordDoc = { job_id: 'farequote_mb', result_type: 'record', probability: 0.0191711, @@ -305,7 +242,7 @@ describe('ML - anomaly utils', () => { }, }; - const multiBucketAnomaly2: AnomalyRecordDoc = { + const multiBucketAnomaly2: MlAnomalyRecordDoc = { job_id: 'farequote_mb2', result_type: 'record', probability: 0.0191711, @@ -326,7 +263,7 @@ describe('ML - anomaly utils', () => { }, }; - const noASEAnomaly: AnomalyRecordDoc = { + const noASEAnomaly: MlAnomalyRecordDoc = { job_id: 'farequote_ase', result_type: 'record', probability: 0.0191711, @@ -341,7 +278,7 @@ describe('ML - anomaly utils', () => { field_name: 'responsetime', }; - const noMBIAnomaly: AnomalyRecordDoc = { + const noMBIAnomaly: MlAnomalyRecordDoc = { job_id: 'farequote_sbi', result_type: 'record', probability: 0.0191711, @@ -362,7 +299,7 @@ describe('ML - anomaly utils', () => { }, }; - const singleBucketAnomaly2: AnomalyRecordDoc = { + const singleBucketAnomaly2: MlAnomalyRecordDoc = { job_id: 'farequote_sb2', result_type: 'record', probability: 0.0191711, diff --git a/x-pack/plugins/ml/common/util/anomaly_utils.ts b/x-pack/packages/ml/anomaly_utils/anomaly_utils.ts similarity index 57% rename from x-pack/plugins/ml/common/util/anomaly_utils.ts rename to x-pack/packages/ml/anomaly_utils/anomaly_utils.ts index 31a2a5fe49ca5..b209a038dd384 100644 --- a/x-pack/plugins/ml/common/util/anomaly_utils.ts +++ b/x-pack/packages/ml/anomaly_utils/anomaly_utils.ts @@ -10,35 +10,67 @@ * to extract information for display in dashboards. */ -import { i18n } from '@kbn/i18n'; -import { CONDITIONS_NOT_SUPPORTED_FUNCTIONS } from '../constants/detector_rule'; -import { ANOMALY_SEVERITY, ANOMALY_THRESHOLD, SEVERITY_COLORS } from '../constants/anomalies'; -import type { AnomaliesTableRecord, AnomalyRecordDoc } from '../types/anomalies'; - -export interface SeverityType { - id: ANOMALY_SEVERITY; - label: string; -} +import type { MlSeverityType } from './anomaly_severity'; +import { ML_ANOMALY_THRESHOLD } from './anomaly_threshold'; +import { ML_ANOMALY_SEVERITY_TYPES } from './anomaly_severity_types'; +import type { MlAnomaliesTableRecord, MlAnomalyRecordDoc } from './types'; +import { ML_DETECTOR_RULE_CONDITIONS_NOT_SUPPORTED_FUNCTIONS } from './detector_rule'; -export enum ENTITY_FIELD_TYPE { +/** + * Enum of entity field types + * @export + * @enum {number} + */ +export enum ML_ENTITY_FIELD_TYPE { BY = 'by', OVER = 'over', PARTITON = 'partition', } -export const ENTITY_FIELD_OPERATIONS = { +/** + * Custom enum of entity field operations + * @type {{ readonly ADD: "+"; readonly REMOVE: "-"; }} + */ +export const ML_ENTITY_FIELD_OPERATIONS = { ADD: '+', REMOVE: '-', } as const; -export type EntityFieldOperation = - typeof ENTITY_FIELD_OPERATIONS[keyof typeof ENTITY_FIELD_OPERATIONS]; +/** + * Union type of entity field operations + * @export + * @typedef {MlEntityFieldOperation} + */ +export type MlEntityFieldOperation = + typeof ML_ENTITY_FIELD_OPERATIONS[keyof typeof ML_ENTITY_FIELD_OPERATIONS]; -export interface EntityField { +/** + * Interface of an entity field + * @export + * @interface MlEntityField + * @typedef {MlEntityField} + */ +export interface MlEntityField { + /** + * The field name + * @type {string} + */ fieldName: string; + /** + * The field value + * @type {(string | number | undefined)} + */ fieldValue: string | number | undefined; - fieldType?: ENTITY_FIELD_TYPE; - operation?: EntityFieldOperation; + /** + * Optional field type + * @type {?ML_ENTITY_FIELD_TYPE} + */ + fieldType?: ML_ENTITY_FIELD_TYPE; + /** + * Optional entity field operation + * @type {?MlEntityFieldOperation} + */ + operation?: MlEntityFieldOperation; } // List of function descriptions for which actual values from record level results should be displayed. @@ -71,153 +103,33 @@ const DISPLAY_TYPICAL_FUNCTIONS = [ 'time', ]; -let severityTypes: Record; - -function getSeverityTypes() { - if (severityTypes) { - return severityTypes; - } - - return (severityTypes = { - critical: { - id: ANOMALY_SEVERITY.CRITICAL, - label: i18n.translate('xpack.ml.anomalyUtils.severity.criticalLabel', { - defaultMessage: 'critical', - }), - }, - major: { - id: ANOMALY_SEVERITY.MAJOR, - label: i18n.translate('xpack.ml.anomalyUtils.severity.majorLabel', { - defaultMessage: 'major', - }), - }, - minor: { - id: ANOMALY_SEVERITY.MINOR, - label: i18n.translate('xpack.ml.anomalyUtils.severity.minorLabel', { - defaultMessage: 'minor', - }), - }, - warning: { - id: ANOMALY_SEVERITY.WARNING, - label: i18n.translate('xpack.ml.anomalyUtils.severity.warningLabel', { - defaultMessage: 'warning', - }), - }, - unknown: { - id: ANOMALY_SEVERITY.UNKNOWN, - label: i18n.translate('xpack.ml.anomalyUtils.severity.unknownLabel', { - defaultMessage: 'unknown', - }), - }, - low: { - id: ANOMALY_SEVERITY.LOW, - label: i18n.translate('xpack.ml.anomalyUtils.severityWithLow.lowLabel', { - defaultMessage: 'low', - }), - }, - }); -} - /** * Returns whether the anomaly is in a categorization analysis. * @param anomaly Anomaly table record */ -export function isCategorizationAnomaly(anomaly: AnomaliesTableRecord): boolean { +export function isCategorizationAnomaly(anomaly: MlAnomaliesTableRecord): boolean { return anomaly.entityName === 'mlcategory'; } -/** - * Returns formatted severity score. - * @param score - A normalized score between 0-100, which is based on the probability of the anomalousness of this record - */ -export function getFormattedSeverityScore(score: number): string { - return score < 1 ? '< 1' : String(parseInt(String(score), 10)); -} - -/** - * Returns a severity label (one of critical, major, minor, warning or unknown) - * for the supplied normalized anomaly score (a value between 0 and 100). - * @param normalizedScore - A normalized score between 0-100, which is based on the probability of the anomalousness of this record - */ -export function getSeverity(normalizedScore: number): SeverityType { - const severityTypesList = getSeverityTypes(); - - if (normalizedScore >= ANOMALY_THRESHOLD.CRITICAL) { - return severityTypesList.critical; - } else if (normalizedScore >= ANOMALY_THRESHOLD.MAJOR) { - return severityTypesList.major; - } else if (normalizedScore >= ANOMALY_THRESHOLD.MINOR) { - return severityTypesList.minor; - } else if (normalizedScore >= ANOMALY_THRESHOLD.LOW) { - return severityTypesList.warning; - } else { - return severityTypesList.unknown; - } -} - -/** - * Returns a severity type (indicating a critical, major, minor, warning or low severity anomaly) - * for the supplied normalized anomaly score (a value between 0 and 100). - * @param normalizedScore - A normalized score between 0-100, which is based on the probability of the anomalousness of this record - */ -export function getSeverityType(normalizedScore: number): ANOMALY_SEVERITY { - if (normalizedScore >= 75) { - return ANOMALY_SEVERITY.CRITICAL; - } else if (normalizedScore >= 50) { - return ANOMALY_SEVERITY.MAJOR; - } else if (normalizedScore >= 25) { - return ANOMALY_SEVERITY.MINOR; - } else if (normalizedScore >= 3) { - return ANOMALY_SEVERITY.WARNING; - } else if (normalizedScore >= 0) { - return ANOMALY_SEVERITY.LOW; - } else { - return ANOMALY_SEVERITY.UNKNOWN; - } -} - /** * Returns a severity label (one of critical, major, minor, warning, low or unknown) * for the supplied normalized anomaly score (a value between 0 and 100), where scores * less than 3 are assigned a severity of 'low'. * @param normalizedScore - A normalized score between 0-100, which is based on the probability of the anomalousness of this record */ -export function getSeverityWithLow(normalizedScore: number): SeverityType { - const severityTypesList = getSeverityTypes(); - - if (normalizedScore >= ANOMALY_THRESHOLD.CRITICAL) { - return severityTypesList.critical; - } else if (normalizedScore >= ANOMALY_THRESHOLD.MAJOR) { - return severityTypesList.major; - } else if (normalizedScore >= ANOMALY_THRESHOLD.MINOR) { - return severityTypesList.minor; - } else if (normalizedScore >= ANOMALY_THRESHOLD.WARNING) { - return severityTypesList.warning; - } else if (normalizedScore >= ANOMALY_THRESHOLD.LOW) { - return severityTypesList.low; - } else { - return severityTypesList.unknown; - } -} - -/** - * Returns a severity RGB color (one of critical, major, minor, warning, low or blank) - * for the supplied normalized anomaly score (a value between 0 and 100). - * @param normalizedScore - A normalized score between 0-100, which is based on the probability of the anomalousness of this record - */ -export function getSeverityColor(normalizedScore: number): string { - if (normalizedScore >= ANOMALY_THRESHOLD.CRITICAL) { - return SEVERITY_COLORS.CRITICAL; - } else if (normalizedScore >= ANOMALY_THRESHOLD.MAJOR) { - return SEVERITY_COLORS.MAJOR; - } else if (normalizedScore >= ANOMALY_THRESHOLD.MINOR) { - return SEVERITY_COLORS.MINOR; - } else if (normalizedScore >= ANOMALY_THRESHOLD.WARNING) { - return SEVERITY_COLORS.WARNING; - } else if (normalizedScore >= ANOMALY_THRESHOLD.LOW) { - return SEVERITY_COLORS.LOW; +export function getSeverityWithLow(normalizedScore: number): MlSeverityType { + if (normalizedScore >= ML_ANOMALY_THRESHOLD.CRITICAL) { + return ML_ANOMALY_SEVERITY_TYPES.critical; + } else if (normalizedScore >= ML_ANOMALY_THRESHOLD.MAJOR) { + return ML_ANOMALY_SEVERITY_TYPES.major; + } else if (normalizedScore >= ML_ANOMALY_THRESHOLD.MINOR) { + return ML_ANOMALY_SEVERITY_TYPES.minor; + } else if (normalizedScore >= ML_ANOMALY_THRESHOLD.WARNING) { + return ML_ANOMALY_SEVERITY_TYPES.warning; + } else if (normalizedScore >= ML_ANOMALY_THRESHOLD.LOW) { + return ML_ANOMALY_SEVERITY_TYPES.low; } else { - return SEVERITY_COLORS.BLANK; + return ML_ANOMALY_SEVERITY_TYPES.unknown; } } @@ -226,7 +138,7 @@ export function getSeverityColor(normalizedScore: number): string { * for example in anomaly charts with a cross-shaped marker. * @param anomaly Anomaly table record */ -export function isMultiBucketAnomaly(anomaly: AnomalyRecordDoc): boolean { +export function isMultiBucketAnomaly(anomaly: MlAnomalyRecordDoc): boolean { if (anomaly.anomaly_score_explanation === undefined) { return false; } @@ -278,7 +190,7 @@ export function getAnomalyScoreExplanationImpactValue(score: number): number { * then partition_field, returning undefined if none of these fields are present. * @param record - anomaly record result for which to obtain the entity field name. */ -export function getEntityFieldName(record: AnomalyRecordDoc): string | undefined { +export function getEntityFieldName(record: MlAnomalyRecordDoc): string | undefined { // Analyses with by and over fields, will have a top-level by_field_name, but // the by_field_value(s) will be in the nested causes array. if (record.by_field_name !== undefined && record.by_field_value !== undefined) { @@ -300,7 +212,7 @@ export function getEntityFieldName(record: AnomalyRecordDoc): string | undefined * then partition_field, returning undefined if none of these fields are present. * @param record - anomaly record result for which to obtain the entity field value. */ -export function getEntityFieldValue(record: AnomalyRecordDoc): string | number | undefined { +export function getEntityFieldValue(record: MlAnomalyRecordDoc): string | number | undefined { if (record.by_field_value !== undefined) { return record.by_field_value; } @@ -319,13 +231,13 @@ export function getEntityFieldValue(record: AnomalyRecordDoc): string | number | * of objects in the form { fieldName: airline, fieldValue: AAL, fieldType: partition } * @param record - anomaly record result for which to obtain the entity field list. */ -export function getEntityFieldList(record: AnomalyRecordDoc): EntityField[] { - const entityFields: EntityField[] = []; +export function getEntityFieldList(record: MlAnomalyRecordDoc): MlEntityField[] { + const entityFields: MlEntityField[] = []; if (record.partition_field_name !== undefined) { entityFields.push({ fieldName: record.partition_field_name, fieldValue: record.partition_field_value, - fieldType: ENTITY_FIELD_TYPE.PARTITON, + fieldType: ML_ENTITY_FIELD_TYPE.PARTITON, }); } @@ -333,7 +245,7 @@ export function getEntityFieldList(record: AnomalyRecordDoc): EntityField[] { entityFields.push({ fieldName: record.over_field_name, fieldValue: record.over_field_value, - fieldType: ENTITY_FIELD_TYPE.OVER, + fieldType: ML_ENTITY_FIELD_TYPE.OVER, }); } @@ -344,7 +256,7 @@ export function getEntityFieldList(record: AnomalyRecordDoc): EntityField[] { entityFields.push({ fieldName: record.by_field_name, fieldValue: record.by_field_value, - fieldType: ENTITY_FIELD_TYPE.BY, + fieldType: ML_ENTITY_FIELD_TYPE.BY, }); } @@ -375,11 +287,11 @@ export function showTypicalForFunction(functionDescription: string): boolean { * Returns whether a rule can be configured against the specified anomaly. * @param record - anomaly record result */ -export function isRuleSupported(record: AnomalyRecordDoc): boolean { +export function isRuleSupported(record: MlAnomalyRecordDoc): boolean { // A rule can be configured with a numeric condition if the function supports it, // and/or with scope if there is a partitioning fields. return ( - CONDITIONS_NOT_SUPPORTED_FUNCTIONS.indexOf(record.function) === -1 || + ML_DETECTOR_RULE_CONDITIONS_NOT_SUPPORTED_FUNCTIONS.indexOf(record.function) === -1 || getEntityFieldName(record) !== undefined ); } @@ -404,6 +316,11 @@ export function isRuleSupported(record: AnomalyRecordDoc): boolean { * function_description field of anomaly records. */ export const aggregationTypeTransform = { + /** + * transform from ML to ES agg type + * @param {string} oldAggType the aggregation type to be transformed + * @returns {string} + */ toES(oldAggType: string): string { let newAggType = oldAggType; @@ -417,6 +334,11 @@ export const aggregationTypeTransform = { return newAggType; }, + /** + * transform from ES to ML agg type + * @param {string} oldAggType the aggregation type to be transformed + * @returns {string} + */ toML(oldAggType: string): string { let newAggType = oldAggType; diff --git a/x-pack/packages/ml/anomaly_utils/constants.ts b/x-pack/packages/ml/anomaly_utils/constants.ts new file mode 100644 index 0000000000000..7825b0f753b66 --- /dev/null +++ b/x-pack/packages/ml/anomaly_utils/constants.ts @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ML_ANOMALY_THRESHOLD } from './anomaly_threshold'; +import { ML_SEVERITY_COLORS } from './severity_colors'; + +/** + * Severity color ramp. + */ +export const ML_SEVERITY_COLOR_RAMP = [ + { + stop: ML_ANOMALY_THRESHOLD.LOW, + color: ML_SEVERITY_COLORS.WARNING, + }, + { + stop: ML_ANOMALY_THRESHOLD.MINOR, + color: ML_SEVERITY_COLORS.MINOR, + }, + { + stop: ML_ANOMALY_THRESHOLD.MAJOR, + color: ML_SEVERITY_COLORS.MAJOR, + }, + { + stop: ML_ANOMALY_THRESHOLD.CRITICAL, + color: ML_SEVERITY_COLORS.CRITICAL, + }, +]; + +/** + * Custom enum for anomaly result type + * @type {{ readonly BUCKET: "bucket"; readonly RECORD: "record"; readonly INFLUENCER: "influencer"; }} + */ +export const ML_ANOMALY_RESULT_TYPE = { + BUCKET: 'bucket', + RECORD: 'record', + INFLUENCER: 'influencer', +} as const; + +/** + * Array of partition fields. + * @type {readonly ["partition_field", "over_field", "by_field"]} + */ +export const ML_PARTITION_FIELDS = ['partition_field', 'over_field', 'by_field'] as const; + +/** + * Machine learning job id attribute name. + * @type {"job_id"} + */ +export const ML_JOB_ID = 'job_id'; + +/** + * Machine learning partition field value attribute name. + * @type {"partition_field_value"} + */ +export const ML_PARTITION_FIELD_VALUE = 'partition_field_value'; diff --git a/x-pack/packages/ml/anomaly_utils/custom_urls.ts b/x-pack/packages/ml/anomaly_utils/custom_urls.ts new file mode 100644 index 0000000000000..1a77dc06c3874 --- /dev/null +++ b/x-pack/packages/ml/anomaly_utils/custom_urls.ts @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import type { MlAnomalyRecordDoc } from './types'; + +/** + * Base Interface for basic custom URL. + * + * @export + * @interface BaseUrlConfig + * @typedef {BaseUrlConfig} + */ +interface BaseUrlConfig { + /** + * The url name of the configuration. + * @type {string} + */ + url_name: string; + /** + * The url value of the configuration. + * @type {string} + */ + url_value: string; +} + +/** + * Extended interface for custom URLs including an optional time range. + * + * @export + * @interface MlKibanaUrlConfig + * @typedef {MlKibanaUrlConfig} + * @extends {BaseUrlConfig} + */ +export interface MlKibanaUrlConfig extends BaseUrlConfig { + /** + * The optional time range for the custom URL configuration + * @type {?string} + */ + time_range?: string; +} + +/** + * Extended interface for custom URLs including a time range. + * + * @export + * @interface MlKibanaUrlConfigWithTimeRange + * @typedef {MlKibanaUrlConfigWithTimeRange} + * @extends {BaseUrlConfig} + */ +export interface MlKibanaUrlConfigWithTimeRange extends BaseUrlConfig { + /** + * The time range for the custom URL configuration + * @type {string} + */ + time_range: string; +} + +/** + * Union type of different custom URL configurations + * + * @export + * @typedef {MlUrlConfig} + */ +export type MlUrlConfig = BaseUrlConfig | MlKibanaUrlConfig; + +/** + * Extended interface of MlAnomalyRecordDoc to include time range information. + * + * @export + * @interface MlCustomUrlAnomalyRecordDoc + * @typedef {MlCustomUrlAnomalyRecordDoc} + * @extends {MlAnomalyRecordDoc} + */ +export interface MlCustomUrlAnomalyRecordDoc extends MlAnomalyRecordDoc { + /** + * The `earliest` timestamp. + * @type {string} + */ + earliest: string; + /** + * The `latest` timestamp. + * @type {string} + */ + latest: string; +} + +/** + * Type guard to idenfity MlKibanaUrlConfigWithTimeRange. + * + * @export + * @param {unknown} arg The unknown type to be evaluated + * @returns {arg is MlKibanaUrlConfigWithTimeRange} whether arg is of type MlKibanaUrlConfigWithTimeRange + */ +export function isMlKibanaUrlConfigWithTimeRange( + arg: unknown +): arg is MlKibanaUrlConfigWithTimeRange { + return ( + isPopulatedObject(arg, ['url_name', 'url_value', 'time_range']) && + typeof arg.time_range === 'string' + ); +} diff --git a/x-pack/plugins/ml/common/constants/detector_rule.ts b/x-pack/packages/ml/anomaly_utils/detector_rule.ts similarity index 50% rename from x-pack/plugins/ml/common/constants/detector_rule.ts rename to x-pack/packages/ml/anomaly_utils/detector_rule.ts index c122de865d74a..a944130d83007 100644 --- a/x-pack/plugins/ml/common/constants/detector_rule.ts +++ b/x-pack/packages/ml/anomaly_utils/detector_rule.ts @@ -9,28 +9,55 @@ * Contains values for ML job detector rules. */ -export enum ACTION { +/** + * Enum ML_DETECTOR_RULE_ACTION + * @export + * @enum {number} + */ +export enum ML_DETECTOR_RULE_ACTION { SKIP_MODEL_UPDATE = 'skip_model_update', SKIP_RESULT = 'skip_result', } -export enum FILTER_TYPE { +/** + * Enum ML_DETECTOR_RULE_FILTER_TYPE + * @export + * @enum {number} + */ +export enum ML_DETECTOR_RULE_FILTER_TYPE { EXCLUDE = 'exclude', INCLUDE = 'include', } -export enum APPLIES_TO { +/** + * Enum ML_DETECTOR_RULE_APPLIES_TO + * @export + * @enum {number} + */ +export enum ML_DETECTOR_RULE_APPLIES_TO { ACTUAL = 'actual', DIFF_FROM_TYPICAL = 'diff_from_typical', TYPICAL = 'typical', } -export enum OPERATOR { +/** + * Enum ML_DETECTOR_RULE_OPERATOR + * @export + * @enum {number} + */ +export enum ML_DETECTOR_RULE_OPERATOR { LESS_THAN = 'lt', LESS_THAN_OR_EQUAL = 'lte', GREATER_THAN = 'gt', GREATER_THAN_OR_EQUAL = 'gte', } -// List of detector functions which don't support rules with numeric conditions. -export const CONDITIONS_NOT_SUPPORTED_FUNCTIONS = ['freq_rare', 'lat_long', 'metric', 'rare']; +/** + * List of detector functions which don't support rules with numeric conditions. + */ +export const ML_DETECTOR_RULE_CONDITIONS_NOT_SUPPORTED_FUNCTIONS = [ + 'freq_rare', + 'lat_long', + 'metric', + 'rare', +]; diff --git a/x-pack/packages/ml/anomaly_utils/get_formatted_severity_score.ts b/x-pack/packages/ml/anomaly_utils/get_formatted_severity_score.ts new file mode 100644 index 0000000000000..19663302f6564 --- /dev/null +++ b/x-pack/packages/ml/anomaly_utils/get_formatted_severity_score.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. + */ + +/** + * Returns formatted severity score. + * @param score - A normalized score between 0-100, which is based on the probability of the anomalousness of this record + */ +export function getFormattedSeverityScore(score: number): string { + return score < 1 ? '< 1' : String(parseInt(String(score), 10)); +} diff --git a/x-pack/packages/ml/anomaly_utils/get_severity.test.ts b/x-pack/packages/ml/anomaly_utils/get_severity.test.ts new file mode 100644 index 0000000000000..c655c0f82cf5c --- /dev/null +++ b/x-pack/packages/ml/anomaly_utils/get_severity.test.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 { getSeverity } from './get_severity'; + +describe('getSeverity', () => { + test('returns warning for 0 <= score < 25', () => { + expect(getSeverity(0).id).toBe('warning'); + expect(getSeverity(0.001).id).toBe('warning'); + expect(getSeverity(24.99).id).toBe('warning'); + }); + + test('returns minor for 25 <= score < 50', () => { + expect(getSeverity(25).id).toBe('minor'); + expect(getSeverity(49.99).id).toBe('minor'); + }); + + test('returns minor for 50 <= score < 75', () => { + expect(getSeverity(50).id).toBe('major'); + expect(getSeverity(74.99).id).toBe('major'); + }); + + test('returns critical for score >= 75', () => { + expect(getSeverity(75).id).toBe('critical'); + expect(getSeverity(100).id).toBe('critical'); + expect(getSeverity(1000).id).toBe('critical'); + }); + + test('returns unknown for scores less than 0', () => { + expect(getSeverity(-10).id).toBe('unknown'); + }); +}); diff --git a/x-pack/packages/ml/anomaly_utils/get_severity.ts b/x-pack/packages/ml/anomaly_utils/get_severity.ts new file mode 100644 index 0000000000000..9259c7f2032cb --- /dev/null +++ b/x-pack/packages/ml/anomaly_utils/get_severity.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ML_ANOMALY_THRESHOLD } from './anomaly_threshold'; +import type { MlSeverityType } from './anomaly_severity'; +import { ML_ANOMALY_SEVERITY_TYPES } from './anomaly_severity_types'; + +/** + * Returns a severity label (one of critical, major, minor, warning or unknown) + * for the supplied normalized anomaly score (a value between 0 and 100). + * @param normalizedScore - A normalized score between 0-100, which is based on the probability of the anomalousness of this record + */ +export function getSeverity(normalizedScore: number): MlSeverityType { + if (normalizedScore >= ML_ANOMALY_THRESHOLD.CRITICAL) { + return ML_ANOMALY_SEVERITY_TYPES.critical; + } else if (normalizedScore >= ML_ANOMALY_THRESHOLD.MAJOR) { + return ML_ANOMALY_SEVERITY_TYPES.major; + } else if (normalizedScore >= ML_ANOMALY_THRESHOLD.MINOR) { + return ML_ANOMALY_SEVERITY_TYPES.minor; + } else if (normalizedScore >= ML_ANOMALY_THRESHOLD.LOW) { + return ML_ANOMALY_SEVERITY_TYPES.warning; + } else { + return ML_ANOMALY_SEVERITY_TYPES.unknown; + } +} diff --git a/x-pack/packages/ml/anomaly_utils/get_severity_color.test.ts b/x-pack/packages/ml/anomaly_utils/get_severity_color.test.ts new file mode 100644 index 0000000000000..64bf7a5284c89 --- /dev/null +++ b/x-pack/packages/ml/anomaly_utils/get_severity_color.test.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getSeverityColor } from './get_severity_color'; + +describe('getSeverityColor', () => { + test('returns correct hex code for low for 0 <= score < 3', () => { + expect(getSeverityColor(0)).toBe('#d2e9f7'); + expect(getSeverityColor(0.001)).toBe('#d2e9f7'); + expect(getSeverityColor(2.99)).toBe('#d2e9f7'); + }); + + test('returns correct hex code for warning for 3 <= score < 25', () => { + expect(getSeverityColor(3)).toBe('#8bc8fb'); + expect(getSeverityColor(24.99)).toBe('#8bc8fb'); + }); + + test('returns correct hex code for minor for 25 <= score < 50', () => { + expect(getSeverityColor(25)).toBe('#fdec25'); + expect(getSeverityColor(49.99)).toBe('#fdec25'); + }); + + test('returns correct hex code for major for 50 <= score < 75', () => { + expect(getSeverityColor(50)).toBe('#fba740'); + expect(getSeverityColor(74.99)).toBe('#fba740'); + }); + + test('returns correct hex code for critical for score >= 75', () => { + expect(getSeverityColor(75)).toBe('#fe5050'); + expect(getSeverityColor(100)).toBe('#fe5050'); + expect(getSeverityColor(1000)).toBe('#fe5050'); + }); + + test('returns correct hex code for unknown for scores less than 0', () => { + expect(getSeverityColor(-10)).toBe('#ffffff'); + }); +}); diff --git a/x-pack/packages/ml/anomaly_utils/get_severity_color.ts b/x-pack/packages/ml/anomaly_utils/get_severity_color.ts new file mode 100644 index 0000000000000..f17609ecef5e7 --- /dev/null +++ b/x-pack/packages/ml/anomaly_utils/get_severity_color.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 { ML_ANOMALY_THRESHOLD } from './anomaly_threshold'; +import { ML_SEVERITY_COLORS } from './severity_colors'; + +/** + * Returns a severity RGB color (one of critical, major, minor, warning, low or blank) + * for the supplied normalized anomaly score (a value between 0 and 100). + * @param normalizedScore - A normalized score between 0-100, which is based on the probability of the anomalousness of this record + */ +export function getSeverityColor(normalizedScore: number): string { + if (normalizedScore >= ML_ANOMALY_THRESHOLD.CRITICAL) { + return ML_SEVERITY_COLORS.CRITICAL; + } else if (normalizedScore >= ML_ANOMALY_THRESHOLD.MAJOR) { + return ML_SEVERITY_COLORS.MAJOR; + } else if (normalizedScore >= ML_ANOMALY_THRESHOLD.MINOR) { + return ML_SEVERITY_COLORS.MINOR; + } else if (normalizedScore >= ML_ANOMALY_THRESHOLD.WARNING) { + return ML_SEVERITY_COLORS.WARNING; + } else if (normalizedScore >= ML_ANOMALY_THRESHOLD.LOW) { + return ML_SEVERITY_COLORS.LOW; + } else { + return ML_SEVERITY_COLORS.BLANK; + } +} diff --git a/x-pack/packages/ml/anomaly_utils/get_severity_type.ts b/x-pack/packages/ml/anomaly_utils/get_severity_type.ts new file mode 100644 index 0000000000000..22093feefff01 --- /dev/null +++ b/x-pack/packages/ml/anomaly_utils/get_severity_type.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ML_ANOMALY_SEVERITY } from './anomaly_severity'; + +/** + * Returns a severity type (indicating a critical, major, minor, warning or low severity anomaly) + * for the supplied normalized anomaly score (a value between 0 and 100). + * @param normalizedScore - A normalized score between 0-100, which is based on the probability of the anomalousness of this record + */ +export function getSeverityType(normalizedScore: number): ML_ANOMALY_SEVERITY { + if (normalizedScore >= 75) { + return ML_ANOMALY_SEVERITY.CRITICAL; + } else if (normalizedScore >= 50) { + return ML_ANOMALY_SEVERITY.MAJOR; + } else if (normalizedScore >= 25) { + return ML_ANOMALY_SEVERITY.MINOR; + } else if (normalizedScore >= 3) { + return ML_ANOMALY_SEVERITY.WARNING; + } else if (normalizedScore >= 0) { + return ML_ANOMALY_SEVERITY.LOW; + } else { + return ML_ANOMALY_SEVERITY.UNKNOWN; + } +} diff --git a/x-pack/packages/ml/anomaly_utils/index.ts b/x-pack/packages/ml/anomaly_utils/index.ts new file mode 100644 index 0000000000000..12ec691ae4dd2 --- /dev/null +++ b/x-pack/packages/ml/anomaly_utils/index.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { type MlSeverityType, ML_ANOMALY_SEVERITY } from './anomaly_severity'; +export { ML_ANOMALY_THRESHOLD } from './anomaly_threshold'; +export { ML_SEVERITY_COLORS } from './severity_colors'; + +export { + aggregationTypeTransform, + getAnomalyScoreExplanationImpactValue, + getEntityFieldList, + getEntityFieldName, + getEntityFieldValue, + getSeverityWithLow, + isCategorizationAnomaly, + isRuleSupported, + isMultiBucketAnomaly, + showActualForFunction, + showTypicalForFunction, + type MlEntityField, + type MlEntityFieldOperation, + ML_ENTITY_FIELD_OPERATIONS, + ML_ENTITY_FIELD_TYPE, +} from './anomaly_utils'; + +export { + ML_ANOMALY_RESULT_TYPE, + ML_PARTITION_FIELD_VALUE, + ML_PARTITION_FIELDS, + ML_JOB_ID, + ML_SEVERITY_COLOR_RAMP, +} from './constants'; + +export { + isMlKibanaUrlConfigWithTimeRange, + type MlCustomUrlAnomalyRecordDoc, + type MlKibanaUrlConfig, + type MlUrlConfig, +} from './custom_urls'; + +export { + ML_DETECTOR_RULE_ACTION, + ML_DETECTOR_RULE_APPLIES_TO, + ML_DETECTOR_RULE_CONDITIONS_NOT_SUPPORTED_FUNCTIONS, + ML_DETECTOR_RULE_FILTER_TYPE, + ML_DETECTOR_RULE_OPERATOR, +} from './detector_rule'; + +export { getFormattedSeverityScore } from './get_formatted_severity_score'; +export { getSeverity } from './get_severity'; +export { getSeverityColor } from './get_severity_color'; +export { getSeverityType } from './get_severity_type'; + +export type { + MlEntityFieldType, + MlInfluencer, + MLAnomalyDoc, + MlAnomalyCategorizerStatsDoc, + MlAnomalyRecordDoc, + MlAnomaliesTableRecord, + MlAnomaliesTableRecordExtended, + MlAnomalyResultType, + MlPartitionFieldsType, + MlRecordForInfluencer, +} from './types'; diff --git a/x-pack/packages/ml/anomaly_utils/jest.config.js b/x-pack/packages/ml/anomaly_utils/jest.config.js new file mode 100644 index 0000000000000..d16d2ef76a83c --- /dev/null +++ b/x-pack/packages/ml/anomaly_utils/jest.config.js @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../..', + roots: ['/x-pack/packages/ml/anomaly_utils'], +}; diff --git a/x-pack/packages/ml/anomaly_utils/kibana.jsonc b/x-pack/packages/ml/anomaly_utils/kibana.jsonc new file mode 100644 index 0000000000000..92fa54e6104de --- /dev/null +++ b/x-pack/packages/ml/anomaly_utils/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/ml-anomaly-utils", + "owner": "@elastic/ml-ui" +} diff --git a/x-pack/packages/ml/anomaly_utils/package.json b/x-pack/packages/ml/anomaly_utils/package.json new file mode 100644 index 0000000000000..f49b343d58cd1 --- /dev/null +++ b/x-pack/packages/ml/anomaly_utils/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/ml-anomaly-utils", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0" +} \ No newline at end of file diff --git a/x-pack/packages/ml/anomaly_utils/severity_colors.ts b/x-pack/packages/ml/anomaly_utils/severity_colors.ts new file mode 100644 index 0000000000000..e871528463399 --- /dev/null +++ b/x-pack/packages/ml/anomaly_utils/severity_colors.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/** + * RGB hex codes used to indicate the severity of an anomaly according to its anomaly score. + */ +export const ML_SEVERITY_COLORS = { + /** + * Color used in the UI to indicate a critical anomaly, with a score greater than or equal to 75. + */ + CRITICAL: '#fe5050', + + /** + * Color used in the UI to indicate a major anomaly, with a score greater than or equal to 50 and less than 75 . + */ + MAJOR: '#fba740', + + /** + * Color used in the UI to indicate a minor anomaly, with a score greater than or equal to 25 and less than 50. + */ + MINOR: '#fdec25', + + /** + * Color used in the UI to indicate a warning anomaly, with a score greater than or equal to 3 and less than 25. + * Note in some parts of the UI, warning severity is used when the score is greater than or equal to 0. + */ + WARNING: '#8bc8fb', + + /** + * Color used in some parts of the UI to indicate a low severity anomaly, with a score greater than or equal to 0 and less than 3. + */ + LOW: '#d2e9f7', + + /** + * Color used in the UI to indicate an anomaly for which the score is unknown. + */ + BLANK: '#ffffff', +}; diff --git a/x-pack/packages/ml/anomaly_utils/tsconfig.json b/x-pack/packages/ml/anomaly_utils/tsconfig.json new file mode 100644 index 0000000000000..2712b8bbffdb3 --- /dev/null +++ b/x-pack/packages/ml/anomaly_utils/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/i18n", + "@kbn/ml-is-populated-object", + ] +} diff --git a/x-pack/plugins/ml/common/types/anomalies.ts b/x-pack/packages/ml/anomaly_utils/types.ts similarity index 78% rename from x-pack/plugins/ml/common/types/anomalies.ts rename to x-pack/packages/ml/anomaly_utils/types.ts index 1bc5ac86ff79a..af87be8f1edf9 100644 --- a/x-pack/plugins/ml/common/types/anomalies.ts +++ b/x-pack/packages/ml/anomaly_utils/types.ts @@ -5,14 +5,14 @@ * 2.0. */ -import { PARTITION_FIELDS, ANOMALY_RESULT_TYPE } from '../constants/anomalies'; -import type { KibanaUrlConfig } from './custom_urls'; +import { ML_PARTITION_FIELDS, ML_ANOMALY_RESULT_TYPE } from './constants'; +import type { MlKibanaUrlConfig } from './custom_urls'; /** * Influencers are the entities that have contributed to, or are to blame for, the anomalies. * Influencer results are available only if an influencer_field_name is specified in the job configuration. */ -export interface Influencer { +export interface MlInfluencer { /** * The field name of the influencer. */ @@ -24,15 +24,26 @@ export interface Influencer { influencer_field_values: string[]; } -export type MLAnomalyDoc = AnomalyRecordDoc; +/** + * Alias of MlAnomalyRecordDoc + * @export + * @typedef {MLAnomalyDoc} + */ +export type MLAnomalyDoc = MlAnomalyRecordDoc; -export type RecordForInfluencer = AnomalyRecordDoc; +/** + * MlRecordForInfluencer, an alias based on MlAnomalyRecordDoc. + * + * @export + * @typedef {MlRecordForInfluencer} + */ +export type MlRecordForInfluencer = MlAnomalyRecordDoc; /** * Anomaly record document. Records contain the detailed analytical results. * They describe the anomalous activity that has been identified in the input data based on the detector configuration. */ -export interface AnomalyRecordDoc { +export interface MlAnomalyRecordDoc { /** * Index signature to cover dynamic attributes added to the record depending on the fields being analyzed. * For example, if the job is analyzing hostname as a by field, then a field hostname is added to the result document. @@ -130,7 +141,7 @@ export interface AnomalyRecordDoc { * If influencers was specified in the detector configuration, this array contains influencers * that contributed to or were to blame for an anomaly. */ - influencers?: Influencer[]; + influencers?: MlInfluencer[]; /** * The field used to split the data. In particular, this property is used for analyzing the splits @@ -238,7 +249,7 @@ export interface AnomalyRecordDoc { /** * Anomaly table record, representing the fields shown in the ML UI anomalies table. */ -export interface AnomaliesTableRecord { +export interface MlAnomaliesTableRecord { /** * The start time of the interval for which the anomaly data in the table is being aggregated. * Anomalies in the table are commonly aggregated by day, hour, or at the bucket span of the job. @@ -248,7 +259,7 @@ export interface AnomaliesTableRecord { /** * The source anomaly record document, containing the full source anomaly record fields. */ - source: AnomalyRecordDoc; + source: MlAnomalyRecordDoc; /** * Unique identifier for the table row. @@ -321,7 +332,7 @@ export interface AnomaliesTableRecord { * List of custom URL drilldowns from the table row to other pages such as * Discover, Dashboard or other web pages. */ - customUrls?: KibanaUrlConfig[]; + customUrls?: MlKibanaUrlConfig[]; /** * Returns true if the anomaly record represented by the table row is for a time series @@ -345,33 +356,131 @@ export interface AnomaliesTableRecord { * and rules length. * Used by the AnomaliesTable component */ -export interface AnomaliesTableRecordExtended extends AnomaliesTableRecord { +export interface MlAnomaliesTableRecordExtended extends MlAnomaliesTableRecord { + /** + * The detector name. + * @type {string} + */ detector: string; + /** + * The length of the rule. + * @type {?number} + */ rulesLength?: number; } -export type PartitionFieldsType = typeof PARTITION_FIELDS[number]; +/** + * Union type for partitiion field types. + * + * @export + * @typedef {MlPartitionFieldsType} + */ +export type MlPartitionFieldsType = typeof ML_PARTITION_FIELDS[number]; -export interface AnomalyCategorizerStatsDoc { +/** + * Anomaly record document for categorizer stats. + * + * @export + * @interface MlAnomalyCategorizerStatsDoc + * @typedef {MlAnomalyCategorizerStatsDoc} + */ +export interface MlAnomalyCategorizerStatsDoc { + /** + * Index signature to cover dynamic attributes added to the record depending on the fields being analyzed. + * For example, if the job is analyzing hostname as a by field, then a field hostname is added to the result document. + */ [key: string]: any; + + /** + * The identifier for the anomaly detection job. + * @type {string} + */ job_id: string; + + /** + * The type of the result document. + * @type {'categorizer_stats'} + */ result_type: 'categorizer_stats'; + + /** + * The field used to segment the analysis. + * When you use this property, you have completely independent baselines for each value of this field. + * @type {?string} + */ partition_field_name?: string; + + /** + * The value of the partition field. + * @type {?string} + */ partition_field_value?: string; + + /** + * The number of documents. + * @type {number} + */ categorized_doc_count: number; + + /** + * The total number of categories. + * @type {number} + */ total_category_count: number; + + /** + * The number of frequent categories. + * @type {number} + */ frequent_category_count: number; + + /** + * The number of rare categories. + * @type {number} + */ rare_category_count: number; + + /** + * The number of dead categories. + * @type {number} + */ dead_category_count: number; + + /** + * The number of failed categories. + * @type {number} + */ failed_category_count: number; + + /** + * The categorization status. + * @type {('ok' | 'warn')} + */ categorization_status: 'ok' | 'warn'; + + /** + * The log time. + * @type {number} + */ log_time: number; + + /** + * The start time of the bucket for which these results were calculated. + * @type {number} + */ timestamp: number; } -export type EntityFieldType = 'partition_field' | 'over_field' | 'by_field'; +/** + * Union type for entity field types. + * + * @export + * @typedef {MlEntityFieldType} + */ +export type MlEntityFieldType = 'partition_field' | 'over_field' | 'by_field'; /** * The type of the anomaly result, such as bucket, influencer or record. */ -export type AnomalyResultType = typeof ANOMALY_RESULT_TYPE[keyof typeof ANOMALY_RESULT_TYPE]; +export type MlAnomalyResultType = + typeof ML_ANOMALY_RESULT_TYPE[keyof typeof ML_ANOMALY_RESULT_TYPE]; diff --git a/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts b/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts index 3376af46daa2b..e1a5d35afdc2e 100644 --- a/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts +++ b/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts @@ -50,14 +50,14 @@ export const BUILT_IN_MODEL_TAG = 'prepackaged'; export const CURATED_MODEL_TAG = 'curated'; export const CURATED_MODEL_DEFINITIONS = { - '.elser_model_1_SNAPSHOT': { + '.elser_model_1': { config: { input: { field_names: ['text_field'], }, }, description: i18n.translate('xpack.ml.trainedModels.modelsList.elserDescription', { - defaultMessage: 'Elastic Learned Sparse EncodeR', + defaultMessage: 'Elastic Learned Sparse EncodeR v1 (Tech Preview)', }), }, } as const; diff --git a/x-pack/plugins/alerting/README.md b/x-pack/plugins/alerting/README.md index e1225d8a9af0b..06188a199ba96 100644 --- a/x-pack/plugins/alerting/README.md +++ b/x-pack/plugins/alerting/README.md @@ -17,6 +17,7 @@ Table of Contents - [Plugin Status](#plugin-status) - [Rule Types](#rule-types) - [Methods](#methods) + - [Alerts as Data](#alerts-as-data) - [Executor](#executor) - [Action variables](#action-variables) - [Recovered Alerts](#recovered-alerts) @@ -38,16 +39,13 @@ Table of Contents > Disclaimer: We are actively working to update the terminology of the Alerting Framework. While all user-facing terminology has been updated, much of the codebase is still a work in progress. - -> References to `rule` and `rule type` entities are still named `AlertType` within the codebase. - **Rule Type**: A function that takes parameters and executes actions on alerts. **Rule**: A configuration that defines a schedule, a rule type w/ parameters, state information and actions. **Alert**: The alert(s) created from a rule execution. -A Kibana rule detects a condition and executes one or more actions when that condition occurs. Rules work by going through the followings steps: +A Kibana rule checks the condition defined by its rule type and executes one or more actions when that condition is met. Rules work by going through the followings steps: 1. Run a periodic check to detect a condition (the check is provided by a rule type). 2. Convert that condition into one or more stateful alerts. @@ -88,20 +86,23 @@ The following table describes the properties of the `options` object. |name|A user-friendly name for the rule type. These will be displayed in dropdowns when choosing rule types.|string| |actionGroups|An explicit list of groups the rule type may schedule actions for, each specifying the ActionGroup's unique ID and human readable name. Each rule type's `actions` validation will use this list to ensure configured groups are valid. We highly encourage using `kbn-i18n` to translate the names of actionGroup when registering the rule type. |Array<{id:string, name:string}>| |defaultActionGroupId|ID value for the default action group for the rule type.|string| -|recoveryActionGroup|The action group to use when an alert goes from an active state to an inactive one. This action group should not be specified under the `actionGroups` property. If no recoveryActionGroup is specified, the default `recovered` action group will be used. |{id:string, name:string}| -|actionVariables|An explicit list of action variables that the rule type makes available via context and state in action parameter templates, and a short human readable description for each. The Alerting UI will use this to display prompts for the users for these variables, in action parameter editors. We highly encourage using `kbn-i18n` to translate the descriptions. |{ context: Array<{name:string, description:string}, state: Array<{name:string, description:string}>| +|recoveryActionGroup|(Optional) The action group to use when an alert goes from an active state to an inactive one. This action group should not be specified under the `actionGroups` property. If no recoveryActionGroup is specified, the default `recovered` action group will be used. |{id:string, name:string}| +|actionVariables|(Optional) An explicit list of action variables that the rule type makes available via context and state in action parameter templates, and a short human readable description for each. The Alerting UI will use this to display prompts for the users for these variables, in action parameter editors. We highly encourage using `kbn-i18n` to translate the descriptions. |{ context: Array<{name:string, description:string}, state: Array<{name:string, description:string}>| |validate.params|When developing a rule type, you can choose to accept a series of parameters. You may also choose to have the parameters validated before they are passed to the `executor` function or created as a saved object. In order to do this, provide a `@kbn/config-schema` schema that we will use to validate the `params` attribute.|@kbn/config-schema| |executor|This is where the code for the rule type lives. This is a function to be called when executing a rule on an interval basis. For full details, see the executor section below.|Function| |producer|The id of the application producing this rule type.|string| |minimumLicenseRequired|The value of a minimum license. Most of the rules are licensed as "basic".|string| -|ruleTaskTimeout|The length of time a rule can run before being cancelled due to timeout. By default, this value is "5m".|string| -|cancelAlertsOnRuleTimeout|Whether to skip writing alerts and scheduling actions if a rule execution is cancelled due to timeout. By default, this value is set to "true".|boolean| +|ruleTaskTimeout|(Optional) The length of time a rule can run before being cancelled due to timeout. If not specified, the default value of "5m" is used.|string| +|cancelAlertsOnRuleTimeout|(Optional) Whether to skip writing alerts and scheduling actions if a rule execution is cancelled due to timeout. If not specified, the default value of "true" is used.|boolean| |useSavedObjectReferences.extractReferences|(Optional) When developing a rule type, you can choose to implement hooks for extracting saved object references from rule parameters. This hook will be invoked when a rule is created or updated. Implementing this hook is optional, but if an extract hook is implemented, an inject hook must also be implemented.|Function |useSavedObjectReferences.injectReferences|(Optional) When developing a rule type, you can choose to implement hooks for injecting saved object references into rule parameters. This hook will be invoked when a rule is retrieved (get or find). Implementing this hook is optional, but if an inject hook is implemented, an extract hook must also be implemented.|Function |isExportable|Whether the rule type is exportable from the Saved Objects Management UI.|boolean| -|defaultScheduleInterval|The default interval that will show up in the UI when creating a rule of this rule type.|boolean| -|doesSetRecoveryContext|Whether the rule type will set context variables for recovered alerts. Defaults to `false`. If this is set to true, context variables are made available for the recovery action group and executors will be provided with the ability to set recovery context.|boolean| +|defaultScheduleInterval|(Optional) The default interval that will show up in the UI when creating a rule of this rule type.|boolean| +|doesSetRecoveryContext|(Optional) Whether the rule type will set context variables for recovered alerts. Defaults to `false`. If this is set to true, context variables are made available for the recovery action group and executors will be provided with the ability to set recovery context.|boolean| |getSummarizedAlerts|(Optional) When developing a rule type, you can choose to implement this hook for retrieving summarized alerts based on execution UUID or time range. This hook will be invoked when an alert summary action is configured for the rule.|Function| +|alerts|(Optional) Specify options for writing alerts as data documents for this rule type. This feature is currently under development so this field is optional but we will eventually make this a requirement of all rule types. For full details, see the alerts as data section below.|IRuleTypeAlerts| +|autoRecoverAlerts|(Optional) Whether the framework should determine if alerts have recovered between rule runs. If not specified, the default value of `true` is used. |boolean| +|getViewInAppRelativeUrl|(Optional) When developing a rule type, you can choose to implement this hook for generating a link back to the Kibana application that can be used in alert actions. If not specified, a generic link back to the Rule Management app is generated.|Function| ### Executor @@ -140,6 +141,56 @@ This is the primary function for a rule type. Whenever the rule needs to execute |rule.throttle|The configured throttle interval for this rule.| |rule.notifyWhen|The configured notification type for this rule.| +### Alerts as Data + +The `alerts` property on a rule type should contain the information needed for the framework to install the Elasticsearch assets required to support writing alert documents. + +|Property|Description|Type| +|---|---|---| +|context|The namespace to use for this rule type. Multiple rule types can specify the same value for their alert context.|string| +|mappings|Specify custom mappings for this rule type. These mappings will be translated into a component template.|ComponentTemplateSpec| +|useEcs|(Optional) Whether to include the ECS component template for this rule type's alerts. If not specified, this value defaults to `false`.|boolean| +|useLegacyAlerts|(Optional) Whether to include the legacy alert component template for this rule type's alerts. This should only be used by rule types that previously registered with the rule registry. If not specified, this value defaults to `false`.|boolean| +|isSpaceAware|(Optional) Whether this rule type's alerts should be space-aware. If set to `true`, space specific alerts indices will be created during rule execution. If not specified, this value defaults to `false`.|boolean| +|secondaryAlias|(Optional) Secondary alias to include. This option is included to support the signals alias for detection rules.|string| + +Regardless of whether any rule type specifies an `alerts` definition, the alerting framework will install the following common Elasticsearch assets on plugin setup: + +|Type|Name|Descripton| +|---|---|---| +|ILM Policy|`.alerts-ilm-policy`|Roll over after 30 days or if index exceeds 50 gigabytes| +|Component Template|`.alerts-framework-mappings`|Includes mappings for all framework alerting fields| +|Component Template|`.alerts-legacy-alert-mappings`|Includes mappings for all legacy alert fields. Use these mappings along with the framework mappings to match the rule registry technical mappings.| +|Component Template|`.alerts-ecs-mappings`|Includes mappings for all ECS fields, excluding those of type `constant_keyword`| + +When a rule type specifies an `alerts` definition, the alerting framework will install Elasticsearch assets for the rule type on plugin setup. The following example definition + +```js +{ + context: `mySpecialRule`, + mappings: { + fieldMap: { + mySpecialRuleField: { + type: 'keyword', + required: false + } + } + }, + useEcs: true +} +``` + +will result in the following assets being installed: + +|Type|Name|Descripton| +|---|---|---| +|Component Template|`.alerts-mySpecialRule-mappings`|Includes mappings for fields in the specified fieldMap| +|Index Template|`.alerts-mySpecialRule.alerts-default-index-template`|Includes references to the framework component template, the `.alerts-mySpecialRule-mappings` component template and the ECS component templates because `useEcs: true`| +|Alias|`.alerts-mySpecialRule.alerts-default`|| +|Index|`.internal.alerts-mySpecialRule.alerts-default-000001`|Concrete write index for the `.alerts-mySpecialRule.alerts-default` alias| + +It is important to note that while multiple rule types can specify the same `context` in order to consolidate their alerts into a single index, the `alerts` definition must be the same across all these rule types. If, for example, RuleTypeA registers alert context `ourRules` with `useEcs: true` and RuleTypeB registers alert context `ourRules` with `useEcs: false`, an error will be thrown during registration. + ### Action Variables The `actionVariables` property should contain the **flattened** names of the state and context variables available when an executor calls `alertInstance.scheduleActions(actionGroup, context)`. These names are meant to be used in prompters in the Alerting UI, are used as text values for display, and can be inserted into to an action parameter text entry field via a UI gesture (e.g., clicking a menu item from a menu built with these names). They should be flattened, so if a state or context variable is an object with properties, these should be listed with the "parent" property/properties in the name, separated by a `.` (period). diff --git a/x-pack/plugins/alerting/public/lib/test_utils.tsx b/x-pack/plugins/alerting/public/lib/test_utils.tsx index 56485d7c88ad1..6e1642bfe0d36 100644 --- a/x-pack/plugins/alerting/public/lib/test_utils.tsx +++ b/x-pack/plugins/alerting/public/lib/test_utils.tsx @@ -11,7 +11,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { I18nProvider } from '@kbn/i18n-react'; import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { render as reactRender, RenderOptions, RenderResult } from '@testing-library/react'; -import { CoreStart } from '@kbn/core/public'; +import { Capabilities, CoreStart } from '@kbn/core/public'; import { coreMock } from '@kbn/core/public/mocks'; import { euiDarkVars } from '@kbn/ui-theme'; import type { ILicense } from '@kbn/licensing-plugin/public'; @@ -22,6 +22,7 @@ import { licensingMock } from '@kbn/licensing-plugin/public/mocks'; type UiRender = (ui: React.ReactElement, options?: RenderOptions) => RenderResult; interface AppMockRendererArgs { + capabilities?: Capabilities; license?: ILicense | null; } @@ -30,9 +31,15 @@ export interface AppMockRenderer { coreStart: CoreStart; queryClient: QueryClient; AppWrapper: React.FC<{ children: React.ReactElement }>; + mocked: { + setBadge: jest.Mock; + }; } -export const createAppMockRenderer = ({ license }: AppMockRendererArgs = {}): AppMockRenderer => { +export const createAppMockRenderer = ({ + capabilities, + license, +}: AppMockRendererArgs = {}): AppMockRenderer => { const theme$ = of({ eui: euiDarkVars, darkMode: true }); const licensingPluginMock = licensingMock.createStart(); @@ -53,13 +60,26 @@ export const createAppMockRenderer = ({ license }: AppMockRendererArgs = {}): Ap error: () => {}, }, }); + + const mockedSetBadge = jest.fn(); const core = coreMock.createStart(); const services = { ...core, + application: { + ...core.application, + capabilities: { + ...core.application.capabilities, + ...capabilities, + }, + }, licensing: license != null ? { ...licensingPluginMock, license$: new BehaviorSubject(license) } : licensingPluginMock, + chrome: { + ...core.chrome, + setBadge: mockedSetBadge, + }, }; const AppWrapper: React.FC<{ children: React.ReactElement }> = React.memo(({ children }) => ( @@ -85,5 +105,8 @@ export const createAppMockRenderer = ({ license }: AppMockRendererArgs = {}): Ap render, queryClient, AppWrapper, + mocked: { + setBadge: mockedSetBadge, + }, }; }; diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/empty_prompt.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/empty_prompt.tsx index 63e7a36c74e06..4b733f1a7289b 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/empty_prompt.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/empty_prompt.tsx @@ -41,7 +41,12 @@ export const EmptyPrompt = React.memo( }, [showCreateButton, onClickCreate, docLinks]); return ( - + ); } ); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/license_prompt.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/license_prompt.tsx index 61de0593e387b..4d1b0588fda4a 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/license_prompt.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/license_prompt.tsx @@ -24,6 +24,7 @@ export const LicensePrompt = React.memo(() => { return ( diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_windows_list.test.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_windows_list.test.tsx index ed3fcb0839441..6cb9708199e0f 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_windows_list.test.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_windows_list.test.tsx @@ -94,7 +94,12 @@ describe('MaintenanceWindowsList', () => { test('it renders', () => { const result = appMockRenderer.render( - {}} loading={false} items={items} /> + {}} + loading={false} + items={items} + readOnly={false} + /> ); expect(result.getAllByTestId('list-item')).toHaveLength(items.length); @@ -115,5 +120,24 @@ describe('MaintenanceWindowsList', () => { // check the endDate formatting expect(result.getAllByText('05/05/23 00:00 AM')).toHaveLength(4); + + // check if action menu is there + expect(result.getAllByTestId('table-actions-icon-button')).toHaveLength(items.length); + }); + + test('it does NOT renders action column in readonly', () => { + const result = appMockRenderer.render( + {}} + loading={false} + items={items} + readOnly={true} + /> + ); + + expect(result.getAllByTestId('list-item')).toHaveLength(items.length); + + // check if action menu is there + expect(result.queryByTestId('table-actions-icon-button')).not.toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_windows_list.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_windows_list.tsx index 705219b9baa2a..7c817a1d70809 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_windows_list.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_windows_list.tsx @@ -32,10 +32,11 @@ import { useFinishAndArchiveMaintenanceWindow } from '../../../hooks/use_finish_ interface MaintenanceWindowsListProps { loading: boolean; items: MaintenanceWindowFindResponse[]; + readOnly: boolean; refreshData: () => void; } -const columns: Array> = [ +const COLUMNS: Array> = [ { field: 'title', name: i18n.NAME, @@ -99,7 +100,7 @@ const search: { filters: SearchFilterConfig[] } = { }; export const MaintenanceWindowsList = React.memo( - ({ loading, items, refreshData }) => { + ({ loading, items, readOnly, refreshData }) => { const { euiTheme } = useEuiTheme(); const { navigateToEditMaintenanceWindows } = useEditMaintenanceWindowsNavigation(); const onEdit = useCallback( @@ -139,32 +140,41 @@ export const MaintenanceWindowsList = React.memo( `; }, [euiTheme.colors.highlight]); - const actions: Array> = [ - { - name: '', - render: ({ status, id }: { status: MaintenanceWindowStatus; id: string }) => { - return ( - - ); + const actions: Array> = useMemo( + () => [ + { + name: '', + render: ({ status, id }: { status: MaintenanceWindowStatus; id: string }) => { + return ( + + ); + }, }, - }, - ]; + ], + [onArchive, onCancel, onCancelAndArchive, onEdit] + ); + + const columns = useMemo( + () => (readOnly ? COLUMNS : COLUMNS.concat(actions)), + [actions, readOnly] + ); return ( ({ + useFindMaintenanceWindows: jest.fn(), +})); + +describe('Maintenance windows page', () => { + let appMockRenderer: AppMockRenderer; + let license = licensingMock.createLicense({ + license: { type: 'platinum' }, + }); + let capabilities: Capabilities = { + [MAINTENANCE_WINDOW_FEATURE_ID]: { + show: true, + save: true, + }, + navLinks: {}, + management: {}, + catalogue: {}, + }; + + beforeEach(() => { + jest.clearAllMocks(); + (useFindMaintenanceWindows as jest.Mock).mockReturnValue({ + isLoading: false, + maintenanceWindows: [], + refetch: jest.fn(), + }); + license = licensingMock.createLicense({ + license: { type: 'platinum' }, + }); + capabilities = { + maintenanceWindow: { + show: true, + save: true, + }, + navLinks: {}, + management: {}, + catalogue: {}, + }; + appMockRenderer = createAppMockRenderer({ capabilities, license }); + }); + + test('show license prompt', () => { + license = licensingMock.createLicense({ + license: { type: 'gold' }, + }); + appMockRenderer = createAppMockRenderer({ capabilities, license }); + const result = appMockRenderer.render(); + expect(result.queryByTestId('mw-license-prompt')).toBeInTheDocument(); + }); + + test('show empty prompt', () => { + const result = appMockRenderer.render(); + expect(result.queryByTestId('mw-empty-prompt')).toBeInTheDocument(); + expect(appMockRenderer.mocked.setBadge).not.toBeCalled(); + }); + + test('show table in read only', () => { + capabilities = { + ...capabilities, + [MAINTENANCE_WINDOW_FEATURE_ID]: { + show: true, + save: false, + }, + }; + appMockRenderer = createAppMockRenderer({ capabilities, license }); + const result = appMockRenderer.render(); + expect(result.queryByTestId('mw-table')).toBeInTheDocument(); + expect(appMockRenderer.mocked.setBadge).toBeCalledTimes(1); + }); +}); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/index.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/index.tsx index ac6d0b5534b9a..5eae620c5a3e5 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/index.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useCallback } from 'react'; +import React, { useCallback, useEffect } from 'react'; import { EuiButton, EuiFlexGroup, @@ -28,9 +28,14 @@ import { CenterJustifiedSpinner } from './components/center_justified_spinner'; import { ExperimentalBadge } from './components/page_header'; import { useLicense } from '../../hooks/use_license'; import { LicensePrompt } from './components/license_prompt'; +import { MAINTENANCE_WINDOW_FEATURE_ID } from '../../../common'; export const MaintenanceWindowsPage = React.memo(() => { - const { docLinks } = useKibana().services; + const { + application: { capabilities }, + chrome, + docLinks, + } = useKibana().services; const { isAtLeastPlatinum } = useLicense(); const { navigateToCreateMaintenanceWindow } = useCreateMaintenanceWindowNavigation(); @@ -44,10 +49,37 @@ export const MaintenanceWindowsPage = React.memo(() => { }, [navigateToCreateMaintenanceWindow]); const refreshData = useCallback(() => refetch(), [refetch]); - - const showEmptyPrompt = !isLoading && maintenanceWindows.length === 0; + const showWindowMaintenance = capabilities[MAINTENANCE_WINDOW_FEATURE_ID].show; + const writeWindowMaintenance = capabilities[MAINTENANCE_WINDOW_FEATURE_ID].save; + const showEmptyPrompt = + !isLoading && + maintenanceWindows.length === 0 && + showWindowMaintenance && + writeWindowMaintenance; const hasLicense = isAtLeastPlatinum(); + const readOnly = showWindowMaintenance && !writeWindowMaintenance; + + // if the user is read only then display the glasses badge in the global navigation header + const setBadge = useCallback(() => { + if (readOnly) { + chrome.setBadge({ + text: i18n.READ_ONLY_BADGE_TEXT, + tooltip: i18n.READ_ONLY_BADGE_TOOLTIP, + iconType: 'glasses', + }); + } + }, [chrome, readOnly]); + + useEffect(() => { + setBadge(); + + // remove the icon after the component unmounts + return () => { + chrome.setBadge(); + }; + }, [setBadge, chrome]); + if (isLoading) { return ; } @@ -71,9 +103,14 @@ export const MaintenanceWindowsPage = React.memo(() => {

{i18n.MAINTENANCE_WINDOWS_DESCRIPTION}

- {!showEmptyPrompt && hasLicense ? ( + {!showEmptyPrompt && hasLicense && writeWindowMaintenance ? ( - + {i18n.CREATE_NEW_BUTTON} @@ -87,6 +124,7 @@ export const MaintenanceWindowsPage = React.memo(() => { <> { const result = render( {}} - value={ANOMALY_SEVERITY.CRITICAL} + value={ML_ANOMALY_SEVERITY.CRITICAL} />, { wrapper: Wrapper } ); diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/index.tsx b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/index.tsx index 1e1ebde4b8e49..b17667921ecde 100644 --- a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { formatAlertEvaluationValue } from '@kbn/observability-plugin/public'; import { @@ -17,8 +17,9 @@ import { ALERT_RULE_UUID, } from '@kbn/rule-data-utils'; import moment from 'moment'; -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useEffect, useMemo } from 'react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { toMicroseconds as toMicrosecondsUtil } from '../../../../../common/utils/formatters'; import { SERVICE_ENVIRONMENT } from '../../../../../common/es_fields/apm'; import { ChartPointerEventContextProvider } from '../../../../context/chart_pointer_event/chart_pointer_event_context'; import { TimeRangeMetadataContextProvider } from '../../../../context/time_range_metadata/time_range_metadata_context'; @@ -35,6 +36,9 @@ import { TRANSACTION_TYPE, } from './types'; +const toMicroseconds = (value?: number) => + value ? toMicrosecondsUtil(value, 'milliseconds') : value; + export function AlertDetailsAppSection({ rule, alert, @@ -64,7 +68,7 @@ export function AlertDetailsAppSection({ ), value: formatAlertEvaluationValue( alert?.fields[ALERT_RULE_TYPE_ID], - alert?.fields[ALERT_EVALUATION_THRESHOLD] + toMicroseconds(alert?.fields[ALERT_EVALUATION_THRESHOLD]) ), }, { @@ -92,7 +96,6 @@ export function AlertDetailsAppSection({ const params = rule.params; const environment = alert.fields[SERVICE_ENVIRONMENT]; const latencyAggregationType = getAggsTypeFromRule(params.aggregationType); - const [latencyMaxY, setLatencyMaxY] = useState(0); // duration is us, convert it to MS const alertDurationMS = alert.fields[ALERT_DURATION]! / 1000; @@ -166,8 +169,8 @@ export function AlertDetailsAppSection({ latencyAggregationType={latencyAggregationType} comparisonEnabled={comparisonEnabled} offset={offset} - setLatencyMaxY={setLatencyMaxY} /> + diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart/latency_chart.tsx b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart/latency_chart.tsx index 1add00e97120a..d4688fc2b0bcc 100644 --- a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart/latency_chart.tsx +++ b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/latency_chart/latency_chart.tsx @@ -45,7 +45,6 @@ function LatencyChart({ comparisonEnabled, offset, timeZone, - setLatencyMaxY, }: { alert: TopAlert; transactionType: string; @@ -58,7 +57,6 @@ function LatencyChart({ comparisonEnabled: boolean; offset: string; timeZone: string; - setLatencyMaxY: React.Dispatch>; }) { const preferred = usePreferredDataSourceAndBucketSize({ start, @@ -150,7 +148,6 @@ function LatencyChart({ comparisonEnabled && isTimeComparison(offset) ? previousPeriod : undefined, ].filter(filterNil); const latencyMaxY = getMaxY(timeseriesLatency); - setLatencyMaxY(latencyMaxY); const latencyFormatter = getDurationFormatter(latencyMaxY); return ( diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/throughput_chart.tsx b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/throughput_chart.tsx index f9891eea5f13f..7881a5004f998 100644 --- a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/throughput_chart.tsx +++ b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/throughput_chart.tsx @@ -17,16 +17,15 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { getDurationFormatter } from '@kbn/observability-plugin/common'; import { ChartType, getTimeSeriesColor, } from '../../../shared/charts/helper/get_timeseries_color'; import { useFetcher } from '../../../../hooks/use_fetcher'; import { TimeseriesChart } from '../../../shared/charts/timeseries_chart'; -import { getResponseTimeTickFormatter } from '../../../shared/charts/transaction_charts/helper'; import { usePreferredDataSourceAndBucketSize } from '../../../../hooks/use_preferred_data_source_and_bucket_size'; import { ApmDocumentType } from '../../../../../common/document_type'; +import { asExactTransactionRate } from '../../../../../common/utils/formatters'; const INITIAL_STATE = { currentPeriod: [], @@ -40,7 +39,6 @@ function ThroughputChart({ end, comparisonChartTheme, comparisonEnabled, - latencyMaxY, offset, timeZone, }: { @@ -51,7 +49,6 @@ function ThroughputChart({ end: string; comparisonChartTheme: RecursivePartial; comparisonEnabled: boolean; - latencyMaxY: number; offset: string; timeZone: string; }) { @@ -118,8 +115,6 @@ function ThroughputChart({ : []), ]; - const latencyFormatter = getDurationFormatter(latencyMaxY); - return ( @@ -154,7 +149,7 @@ function ThroughputChart({ fetchStatus={statusThroughput} customTheme={comparisonChartTheme} timeseries={timeseriesThroughput} - yLabelFormat={getResponseTimeTickFormatter(latencyFormatter)} + yLabelFormat={asExactTransactionRate} timeZone={timeZone} /> diff --git a/x-pack/plugins/apm/public/components/shared/charts/helper/get_chart_anomaly_timeseries.tsx b/x-pack/plugins/apm/public/components/shared/charts/helper/get_chart_anomaly_timeseries.tsx index 8d16de3f01376..b238d924be98e 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/helper/get_chart_anomaly_timeseries.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/helper/get_chart_anomaly_timeseries.tsx @@ -8,12 +8,10 @@ import { i18n } from '@kbn/i18n'; import { rgba } from 'polished'; import { EuiTheme } from '@kbn/kibana-react-plugin/common'; -import { getSeverity } from '@kbn/ml-plugin/public'; +import { getSeverity } from '@kbn/ml-anomaly-utils/get_severity'; +import { ML_ANOMALY_SEVERITY } from '@kbn/ml-anomaly-utils/anomaly_severity'; +import { ML_ANOMALY_THRESHOLD } from '@kbn/ml-anomaly-utils/anomaly_threshold'; import { getSeverityColor } from '../../../../../common/anomaly_detection'; -import { - ANOMALY_SEVERITY, - ANOMALY_THRESHOLD, -} from '../../../../../common/ml_constants'; import { ServiceAnomalyTimeseries } from '../../../../../common/anomaly_detection/service_anomaly_timeseries'; import { APMChartSpec } from '../../../../../typings/timeseries'; @@ -61,10 +59,13 @@ export function getChartAnomalyTimeseries({ ]; const severities = [ - { severity: ANOMALY_SEVERITY.MAJOR, threshold: ANOMALY_THRESHOLD.MAJOR }, { - severity: ANOMALY_SEVERITY.CRITICAL, - threshold: ANOMALY_THRESHOLD.CRITICAL, + severity: ML_ANOMALY_SEVERITY.MAJOR, + threshold: ML_ANOMALY_THRESHOLD.MAJOR, + }, + { + severity: ML_ANOMALY_SEVERITY.CRITICAL, + threshold: ML_ANOMALY_THRESHOLD.CRITICAL, }, ]; diff --git a/x-pack/plugins/apm/server/routes/alerts/rule_types/anomaly/register_anomaly_rule_type.test.ts b/x-pack/plugins/apm/server/routes/alerts/rule_types/anomaly/register_anomaly_rule_type.test.ts index 875dce26c40fc..ca504ec6b353b 100644 --- a/x-pack/plugins/apm/server/routes/alerts/rule_types/anomaly/register_anomaly_rule_type.test.ts +++ b/x-pack/plugins/apm/server/routes/alerts/rule_types/anomaly/register_anomaly_rule_type.test.ts @@ -5,7 +5,7 @@ * 2.0. */ import { registerAnomalyRuleType } from './register_anomaly_rule_type'; -import { ANOMALY_SEVERITY } from '../../../../../common/ml_constants'; +import { ML_ANOMALY_SEVERITY } from '@kbn/ml-anomaly-utils/anomaly_severity'; import { MlPluginSetup } from '@kbn/ml-plugin/server'; import * as GetServiceAnomalies from '../../../service_map/get_service_anomalies'; import { createRuleTypeMocks } from '../../test_utils'; @@ -24,7 +24,7 @@ describe('Transaction duration anomaly alert', () => { ml: undefined, }); - const params = { anomalySeverityType: ANOMALY_SEVERITY.MINOR }; + const params = { anomalySeverityType: ML_ANOMALY_SEVERITY.MINOR }; await executor({ params }); @@ -52,7 +52,7 @@ describe('Transaction duration anomaly alert', () => { ml, }); - const params = { anomalySeverityType: ANOMALY_SEVERITY.MINOR }; + const params = { anomalySeverityType: ML_ANOMALY_SEVERITY.MINOR }; await executor({ params }); expect( @@ -103,7 +103,7 @@ describe('Transaction duration anomaly alert', () => { ml, }); - const params = { anomalySeverityType: ANOMALY_SEVERITY.MINOR }; + const params = { anomalySeverityType: ML_ANOMALY_SEVERITY.MINOR }; await executor({ params }); @@ -180,7 +180,7 @@ describe('Transaction duration anomaly alert', () => { }); const params = { - anomalySeverityType: ANOMALY_SEVERITY.MINOR, + anomalySeverityType: ML_ANOMALY_SEVERITY.MINOR, windowSize: 5, windowUnit: 'm', }; diff --git a/x-pack/plugins/apm/tsconfig.json b/x-pack/plugins/apm/tsconfig.json index e6cada76ab52f..68452bf5e97f8 100644 --- a/x-pack/plugins/apm/tsconfig.json +++ b/x-pack/plugins/apm/tsconfig.json @@ -84,6 +84,7 @@ "@kbn/logging-mocks", "@kbn/chart-icons", "@kbn/observability-shared-plugin", + "@kbn/ml-anomaly-utils", "@kbn/shared-ux-prompt-not-found", ], "exclude": [ diff --git a/x-pack/plugins/cases/common/api/cases/user_actions/operations/find.ts b/x-pack/plugins/cases/common/api/cases/user_actions/operations/find.ts index dc4524e2bf3e9..ce107c73010c1 100644 --- a/x-pack/plugins/cases/common/api/cases/user_actions/operations/find.ts +++ b/x-pack/plugins/cases/common/api/cases/user_actions/operations/find.ts @@ -6,7 +6,7 @@ */ import * as rt from 'io-ts'; -import { CaseUserActionsResponseRt } from '../response'; +import { UserActionsRt } from '../response'; import { ActionTypes } from '../common'; import { NumberFromString } from '../../../saved_object'; @@ -36,7 +36,7 @@ export const UserActionFindRequestRt = rt.partial({ export type UserActionFindRequest = rt.TypeOf; export const UserActionFindResponseRt = rt.type({ - userActions: CaseUserActionsResponseRt, + userActions: UserActionsRt, page: rt.number, perPage: rt.number, total: rt.number, diff --git a/x-pack/plugins/cases/common/api/cases/user_actions/response.ts b/x-pack/plugins/cases/common/api/cases/user_actions/response.ts index 8f0ef5517db4f..e4d1e681b1572 100644 --- a/x-pack/plugins/cases/common/api/cases/user_actions/response.ts +++ b/x-pack/plugins/cases/common/api/cases/user_actions/response.ts @@ -39,7 +39,7 @@ const CommonUserActionsRt = rt.union([ AssigneesUserActionRt, ]); -export const UserActionsRt = rt.union([ +const UserActionPayloadRt = rt.union([ CommonUserActionsRt, CreateCaseUserActionRt, ConnectorUserActionRt, @@ -55,7 +55,7 @@ const UserActionsWithoutConnectorIdRt = rt.union([ DeleteCaseUserActionRt, ]); -const CaseUserActionBasicRt = rt.intersection([UserActionsRt, UserActionCommonAttributesRt]); +const CaseUserActionBasicRt = rt.intersection([UserActionPayloadRt, UserActionCommonAttributesRt]); const CaseUserActionBasicWithoutConnectorIdRt = rt.intersection([ UserActionsWithoutConnectorIdRt, UserActionCommonAttributesRt, @@ -69,38 +69,38 @@ const CaseUserActionDeprecatedResponseRt = rt.intersection([ /** * This includes the comment_id but not the action_id or case_id */ -const CaseUserActionInjectedAttributesRt = rt.intersection([ - CaseUserActionBasicRt, - CaseUserActionInjectedIdsRt, -]); +const UserActionAttributes = rt.intersection([CaseUserActionBasicRt, CaseUserActionInjectedIdsRt]); -const CaseUserActionResponseRt = rt.intersection([ - CaseUserActionInjectedAttributesRt, +const UserActionRt = rt.intersection([ + UserActionAttributes, rt.type({ id: rt.string, version: rt.string, }), ]); -const CaseUserActionAttributesRt = CaseUserActionBasicRt; -export const CaseUserActionsResponseRt = rt.array(CaseUserActionResponseRt); +export const UserActionsRt = rt.array(UserActionRt); export const CaseUserActionsDeprecatedResponseRt = rt.array(CaseUserActionDeprecatedResponseRt); export const CaseUserActionStatsResponseRt = CaseUserActionStatsRt; -export type CaseUserActionAttributes = rt.TypeOf; export type CaseUserActionAttributesWithoutConnectorId = rt.TypeOf< typeof CaseUserActionBasicWithoutConnectorIdRt >; export type CaseUserActionStatsResponse = rt.TypeOf; -export type CaseUserActionsResponse = rt.TypeOf; -export type CaseUserActionResponse = rt.TypeOf; +export type UserActions = rt.TypeOf; +export type UserAction = rt.TypeOf; export type CaseUserActionsDeprecatedResponse = rt.TypeOf< typeof CaseUserActionsDeprecatedResponseRt >; export type CaseUserActionDeprecatedResponse = rt.TypeOf; -export type CaseUserActionInjectedAttributes = rt.TypeOf; +export type UserActionAttributes = rt.TypeOf; -export type UserAction = rt.TypeOf; +/** + * This defines the high level category for the user action. Whether the user add, removed, updated something + */ +export type ActionCategory = rt.TypeOf; +/** + * This defines the type of the user action, meaning what individual action was taken, for example changing the status, + * adding an assignee etc. + */ export type UserActionTypes = ActionTypeValues; - -export type CaseUserAction = rt.TypeOf; diff --git a/x-pack/plugins/cases/common/ui/types.ts b/x-pack/plugins/cases/common/ui/types.ts index c513f5a8e2337..c8a1e2172651f 100644 --- a/x-pack/plugins/cases/common/ui/types.ts +++ b/x-pack/plugins/cases/common/ui/types.ts @@ -17,7 +17,7 @@ import type { CaseStatuses, User, ActionConnector, - CaseUserActionResponse, + UserAction, SingleCaseMetricsResponse, CommentResponse, Case as CaseSnakeCase, @@ -87,9 +87,9 @@ export type Comment = SnakeToCamelCase; export type AlertComment = SnakeToCamelCase; export type ExternalReferenceComment = SnakeToCamelCase; export type PersistableComment = SnakeToCamelCase; -export type CaseUserActions = SnakeToCamelCase; +export type UserActionUI = SnakeToCamelCase; export type FindCaseUserActions = Omit, 'userActions'> & { - userActions: CaseUserActions[]; + userActions: UserActionUI[]; }; export type CaseUserActionsStats = SnakeToCamelCase; export type CaseUI = Omit, 'comments'> & { comments: Comment[] }; diff --git a/x-pack/plugins/cases/public/api/utils.ts b/x-pack/plugins/cases/public/api/utils.ts index d463fb4304249..4f2dde86dfec1 100644 --- a/x-pack/plugins/cases/public/api/utils.ts +++ b/x-pack/plugins/cases/public/api/utils.ts @@ -14,7 +14,7 @@ import { import type { CasesFindResponse, Case, - CaseUserActionsResponse, + UserActions, CommentRequest, CommentResponse, CaseResolveResponse, @@ -81,7 +81,7 @@ export const convertAttachmentToCamelCase = (attachment: CommentRequest): Commen return convertToCamelCase(attachment); }; -export const convertUserActionsToCamelCase = (userActions: CaseUserActionsResponse) => { +export const convertUserActionsToCamelCase = (userActions: UserActions) => { return userActions.map((userAction) => { if (isCommentUserAction(userAction)) { const userActionWithoutPayload = omit(userAction, 'payload.comment'); diff --git a/x-pack/plugins/cases/public/components/all_cases/translations.ts b/x-pack/plugins/cases/public/components/all_cases/translations.ts index 80554bb44fb39..12c16e1f6501d 100644 --- a/x-pack/plugins/cases/public/components/all_cases/translations.ts +++ b/x-pack/plugins/cases/public/components/all_cases/translations.ts @@ -52,7 +52,7 @@ export const BULK_ACTIONS = i18n.translate('xpack.cases.caseTable.bulkActions', }); export const EXTERNAL_INCIDENT = i18n.translate('xpack.cases.caseTable.snIncident', { - defaultMessage: 'External Incident', + defaultMessage: 'External incident', }); export const SEVERITY = i18n.translate('xpack.cases.caseTable.severity', { @@ -60,7 +60,7 @@ export const SEVERITY = i18n.translate('xpack.cases.caseTable.severity', { }); export const INCIDENT_MANAGEMENT_SYSTEM = i18n.translate('xpack.cases.caseTable.incidentSystem', { - defaultMessage: 'Incident Management System', + defaultMessage: 'Incident management system', }); export const SEARCH_PLACEHOLDER = i18n.translate('xpack.cases.caseTable.searchPlaceholder', { diff --git a/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.test.tsx b/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.test.tsx index 060ac64cafb08..f6cbe9113e5d6 100644 --- a/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.test.tsx @@ -99,7 +99,7 @@ describe('useCasesColumns ', () => { "sortable": true, }, Object { - "name": "External Incident", + "name": "External incident", "render": [Function], "width": undefined, }, @@ -232,7 +232,7 @@ describe('useCasesColumns ', () => { "sortable": true, }, Object { - "name": "External Incident", + "name": "External incident", "render": [Function], "width": undefined, }, @@ -316,7 +316,7 @@ describe('useCasesColumns ', () => { "sortable": true, }, Object { - "name": "External Incident", + "name": "External incident", "render": [Function], "width": undefined, }, @@ -395,7 +395,7 @@ describe('useCasesColumns ', () => { "sortable": true, }, Object { - "name": "External Incident", + "name": "External incident", "render": [Function], "width": undefined, }, @@ -479,7 +479,7 @@ describe('useCasesColumns ', () => { "sortable": true, }, Object { - "name": "External Incident", + "name": "External incident", "render": [Function], "width": undefined, }, @@ -638,7 +638,7 @@ describe('useCasesColumns ', () => { "sortable": true, }, Object { - "name": "External Incident", + "name": "External incident", "render": [Function], "width": undefined, }, diff --git a/x-pack/plugins/cases/public/components/files/file_type.test.tsx b/x-pack/plugins/cases/public/components/files/file_type.test.tsx index 8d2c782ee0aae..0fc01442487f4 100644 --- a/x-pack/plugins/cases/public/components/files/file_type.test.tsx +++ b/x-pack/plugins/cases/public/components/files/file_type.test.tsx @@ -78,7 +78,7 @@ describe('getFileType', () => { expect(actions[0]).toStrictEqual({ type: AttachmentActionType.CUSTOM, isPrimary: false, - label: 'Download File', + label: 'Download file', render: expect.any(Function), }); @@ -102,7 +102,7 @@ describe('getFileType', () => { expect(actions[1]).toStrictEqual({ type: AttachmentActionType.CUSTOM, isPrimary: false, - label: 'Delete File', + label: 'Delete file', render: expect.any(Function), }); @@ -126,7 +126,7 @@ describe('getFileType', () => { expect(actions[1]).toStrictEqual({ type: AttachmentActionType.CUSTOM, isPrimary: false, - label: 'Delete File', + label: 'Delete file', render: expect.any(Function), }); diff --git a/x-pack/plugins/cases/public/components/files/translations.tsx b/x-pack/plugins/cases/public/components/files/translations.tsx index 2fd8ed28c6aa4..b500d140217f0 100644 --- a/x-pack/plugins/cases/public/components/files/translations.tsx +++ b/x-pack/plugins/cases/public/components/files/translations.tsx @@ -12,7 +12,7 @@ export const ACTIONS = i18n.translate('xpack.cases.caseView.files.actions', { }); export const ADD_FILE = i18n.translate('xpack.cases.caseView.files.addFile', { - defaultMessage: 'Add File', + defaultMessage: 'Add file', }); export const CLOSE_MODAL = i18n.translate('xpack.cases.caseView.files.closeModal', { @@ -20,15 +20,15 @@ export const CLOSE_MODAL = i18n.translate('xpack.cases.caseView.files.closeModal }); export const DATE_ADDED = i18n.translate('xpack.cases.caseView.files.dateAdded', { - defaultMessage: 'Date Added', + defaultMessage: 'Date added', }); export const DELETE_FILE = i18n.translate('xpack.cases.caseView.files.deleteFile', { - defaultMessage: 'Delete File', + defaultMessage: 'Delete file', }); export const DOWNLOAD_FILE = i18n.translate('xpack.cases.caseView.files.downloadFile', { - defaultMessage: 'Download File', + defaultMessage: 'Download file', }); export const FILES_TABLE = i18n.translate('xpack.cases.caseView.files.filesTable', { diff --git a/x-pack/plugins/cases/public/components/files/use_files_table_columns.test.tsx b/x-pack/plugins/cases/public/components/files/use_files_table_columns.test.tsx index 77070da0dbc57..0205fac1d4ec4 100644 --- a/x-pack/plugins/cases/public/components/files/use_files_table_columns.test.tsx +++ b/x-pack/plugins/cases/public/components/files/use_files_table_columns.test.tsx @@ -47,18 +47,18 @@ describe('useFilesTableColumns', () => { "data-test-subj": "cases-files-table-date-added", "dataType": "date", "field": "created", - "name": "Date Added", + "name": "Date added", }, Object { "actions": Array [ Object { - "description": "Download File", + "description": "Download file", "isPrimary": true, "name": "Download", "render": [Function], }, Object { - "description": "Delete File", + "description": "Delete file", "isPrimary": true, "name": "Delete", "render": [Function], diff --git a/x-pack/plugins/cases/public/components/user_actions/common.tsx b/x-pack/plugins/cases/public/components/user_actions/common.tsx index 4d04f118bebe4..800831b4cc9b2 100644 --- a/x-pack/plugins/cases/public/components/user_actions/common.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/common.tsx @@ -9,7 +9,7 @@ import React from 'react'; import type { EuiCommentProps } from '@elastic/eui'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import type { ConnectorUserAction, UserAction } from '../../../common/api'; +import type { ConnectorUserAction, ActionCategory } from '../../../common/api'; import { Actions } from '../../../common/api'; import { UserActionTimestamp } from './timestamp'; import type { UserActionBuilder, UserActionBuilderArgs, UserActionResponse } from './types'; @@ -22,8 +22,10 @@ interface Props { handleOutlineComment: (id: string) => void; } -const showMoveToReference = (action: UserAction, commentId: string | null): commentId is string => - action === Actions.update && commentId != null; +const showMoveToReference = ( + action: ActionCategory, + commentId: string | null +): commentId is string => action === Actions.update && commentId != null; const CommentListActions: React.FC = React.memo(({ userAction, handleOutlineComment }) => ( diff --git a/x-pack/plugins/cases/public/components/user_actions/types.ts b/x-pack/plugins/cases/public/components/user_actions/types.ts index f8ff85e05bf18..4e2660734052f 100644 --- a/x-pack/plugins/cases/public/components/user_actions/types.ts +++ b/x-pack/plugins/cases/public/components/user_actions/types.ts @@ -12,7 +12,7 @@ import type { ActionTypes, UserActionWithResponse } from '../../../common/api'; import type { CaseUI, CaseConnectors, - CaseUserActions, + UserActionUI, Comment, UseFetchAlertData, CaseUserActionsStats, @@ -54,7 +54,7 @@ export interface UserActionBuilderArgs { externalReferenceAttachmentTypeRegistry: ExternalReferenceAttachmentTypeRegistry; persistableStateAttachmentTypeRegistry: PersistableStateAttachmentTypeRegistry; caseConnectors: CaseConnectors; - userAction: CaseUserActions; + userAction: UserActionUI; comments: Comment[]; index: number; commentRefs: React.MutableRefObject< diff --git a/x-pack/plugins/cases/public/components/user_actions/use_user_actions_last_page.tsx b/x-pack/plugins/cases/public/components/user_actions/use_user_actions_last_page.tsx index 800f1f94c4832..32fc45aa433ab 100644 --- a/x-pack/plugins/cases/public/components/user_actions/use_user_actions_last_page.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/use_user_actions_last_page.tsx @@ -7,7 +7,7 @@ import { useMemo } from 'react'; -import type { CaseUserActions } from '../../containers/types'; +import type { UserActionUI } from '../../containers/types'; import { useFindCaseUserActions } from '../../containers/use_find_case_user_actions'; import type { UserActivityParams } from '../user_actions_activity_bar/types'; @@ -25,7 +25,7 @@ export const useLastPageUserActions = ({ const { data: lastPageUserActionsData, isLoading: isLoadingLastPageUserActions } = useFindCaseUserActions(caseId, { ...userActivityQueryParams, page: lastPage }, lastPage > 1); - const lastPageUserActions = useMemo(() => { + const lastPageUserActions = useMemo(() => { if (isLoadingLastPageUserActions || !lastPageUserActionsData) { return []; } diff --git a/x-pack/plugins/cases/public/components/user_actions/use_user_actions_pagination.tsx b/x-pack/plugins/cases/public/components/user_actions/use_user_actions_pagination.tsx index ec9b931af0ab2..bdaaa68de0157 100644 --- a/x-pack/plugins/cases/public/components/user_actions/use_user_actions_pagination.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/use_user_actions_pagination.tsx @@ -8,7 +8,7 @@ import { useMemo } from 'react'; import { useInfiniteFindCaseUserActions } from '../../containers/use_infinite_find_case_user_actions'; -import type { CaseUserActions } from '../../containers/types'; +import type { UserActionUI } from '../../containers/types'; import type { UserActivityParams } from '../user_actions_activity_bar/types'; interface UserActionsPagination { @@ -32,12 +32,12 @@ export const useUserActionsPagination = ({ const showBottomList = lastPage > 1; - const infiniteCaseUserActions = useMemo(() => { + const infiniteCaseUserActions = useMemo(() => { if (!caseInfiniteUserActionsData?.pages?.length || isLoadingInfiniteUserActions) { return []; } - const userActionsData: CaseUserActions[] = []; + const userActionsData: UserActionUI[] = []; caseInfiniteUserActionsData.pages.forEach((page) => userActionsData.push(...page.userActions)); diff --git a/x-pack/plugins/cases/public/components/user_actions/user_actions_list.tsx b/x-pack/plugins/cases/public/components/user_actions/user_actions_list.tsx index 72d8371734548..1b4508291bfc7 100644 --- a/x-pack/plugins/cases/public/components/user_actions/user_actions_list.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/user_actions_list.tsx @@ -11,7 +11,7 @@ import { EuiCommentList } from '@elastic/eui'; import React, { useMemo, useEffect, useState } from 'react'; import styled from 'styled-components'; -import type { CaseUserActions } from '../../containers/types'; +import type { UserActionUI } from '../../containers/types'; import type { UserActionBuilderArgs, UserActionTreeProps } from './types'; import { isUserActionTypeSupported } from './helpers'; import { useCasesContext } from '../cases_context/use_cases_context'; @@ -78,7 +78,7 @@ export type UserActionListProps = Omit< | 'statusActionButton' > & Pick & { - caseUserActions: CaseUserActions[]; + caseUserActions: UserActionUI[]; loadingAlertData: boolean; manualAlertsData: Record; bottomActions?: EuiCommentProps[]; diff --git a/x-pack/plugins/cases/public/containers/api.ts b/x-pack/plugins/cases/public/containers/api.ts index 2d9e17658d156..c804d1d34f60c 100644 --- a/x-pack/plugins/cases/public/containers/api.ts +++ b/x-pack/plugins/cases/public/containers/api.ts @@ -72,7 +72,7 @@ import type { CaseUI, SingleCaseMetrics, SingleCaseMetricsFeature, - CaseUserActions, + UserActionUI, } from './types'; import { @@ -186,7 +186,7 @@ export const findCaseUserActions = async ( ...response, userActions: convertUserActionsToCamelCase( decodeCaseUserActionsResponse(response.userActions) - ) as CaseUserActions[], + ) as UserActionUI[], }; }; diff --git a/x-pack/plugins/cases/public/containers/mock.ts b/x-pack/plugins/cases/public/containers/mock.ts index d2ff6a1c0de6f..d35d001d41bc4 100644 --- a/x-pack/plugins/cases/public/containers/mock.ts +++ b/x-pack/plugins/cases/public/containers/mock.ts @@ -6,14 +6,7 @@ */ import type { FileJSON } from '@kbn/shared-ux-file-types'; -import type { - ActionLicense, - CasesUI, - CaseUI, - CasesStatus, - CaseUserActions, - Comment, -} from './types'; +import type { ActionLicense, CasesUI, CaseUI, CasesStatus, UserActionUI, Comment } from './types'; import type { ResolvedCase, @@ -33,10 +26,10 @@ import type { CasesFindResponse, Cases, CasesStatusResponse, - CaseUserActionResponse, - CaseUserActionsResponse, - CommentResponse, UserAction, + UserActions, + CommentResponse, + ActionCategory, UserActionTypes, UserActionWithResponse, CommentUserAction, @@ -618,9 +611,9 @@ export const allCasesSnake: CasesFindResponse = { export const getUserAction = ( type: UserActionTypes, - action: UserAction, + action: ActionCategory, overrides?: Record -): CaseUserActions => { +): UserActionUI => { const commonProperties = { ...basicAction, id: `${type}-${action}`, @@ -745,27 +738,27 @@ export const getUserAction = ( return { ...commonProperties, ...overrides, - } as CaseUserActions; + } as UserActionUI; } }; export const getUserActionSnake = ( type: UserActionTypes, - action: UserAction, + action: ActionCategory, overrides?: Record -): CaseUserActionResponse => { +): UserAction => { return { ...covertToSnakeCase(getUserAction(type, action, overrides)), - } as unknown as CaseUserActionResponse; + } as unknown as UserAction; }; -export const caseUserActionsSnake: CaseUserActionsResponse = [ +export const caseUserActionsSnake: UserActions = [ getUserActionSnake('description', Actions.create), getUserActionSnake('comment', Actions.create), getUserActionSnake('description', Actions.update), ]; -export const caseUserActionsWithRegisteredAttachmentsSnake: CaseUserActionsResponse = [ +export const caseUserActionsWithRegisteredAttachmentsSnake: UserActions = [ getUserActionSnake('description', Actions.create), { created_at: basicCreatedAt, @@ -865,13 +858,13 @@ export const getHostIsolationUserAction = ( ...overrides, }); -export const caseUserActions: CaseUserActions[] = [ +export const caseUserActions: UserActionUI[] = [ getUserAction('description', Actions.create), getUserAction('comment', Actions.create), getUserAction('description', Actions.update), ]; -export const caseUserActionsWithRegisteredAttachments: CaseUserActions[] = [ +export const caseUserActionsWithRegisteredAttachments: UserActionUI[] = [ getUserAction('description', Actions.create), { createdAt: basicCreatedAt, diff --git a/x-pack/plugins/cases/public/containers/utils.ts b/x-pack/plugins/cases/public/containers/utils.ts index 66b540040415f..c1d89dc81595c 100644 --- a/x-pack/plugins/cases/public/containers/utils.ts +++ b/x-pack/plugins/cases/public/containers/utils.ts @@ -15,7 +15,7 @@ import { NO_ASSIGNEES_FILTERING_KEYWORD } from '../../common/constants'; import type { CasesConfigurationsResponse, CasesConfigureResponse, - CaseUserActionsResponse, + UserActions, CasePatchRequest, CaseResolveResponse, SingleCaseMetricsResponse, @@ -30,7 +30,7 @@ import { throwErrors, CaseConfigurationsResponseRt, CaseConfigureResponseRt, - CaseUserActionsResponseRt, + UserActionsRt, CommentType, CaseResolveResponseRt, SingleCaseMetricsResponseRt, @@ -80,11 +80,8 @@ export const decodeCaseConfigureResponse = (respCase?: CasesConfigureResponse) = fold(throwErrors(createToasterPlainError), identity) ); -export const decodeCaseUserActionsResponse = (respUserActions?: CaseUserActionsResponse) => - pipe( - CaseUserActionsResponseRt.decode(respUserActions), - fold(throwErrors(createToasterPlainError), identity) - ); +export const decodeCaseUserActionsResponse = (respUserActions?: UserActions) => + pipe(UserActionsRt.decode(respUserActions), fold(throwErrors(createToasterPlainError), identity)); export const decodeCaseUserActionStatsResponse = ( caseUserActionsStats: CaseUserActionStatsResponse diff --git a/x-pack/plugins/cases/server/client/metrics/lifespan.test.ts b/x-pack/plugins/cases/server/client/metrics/lifespan.test.ts index 808b6f334e15f..a36e9eb8a106b 100644 --- a/x-pack/plugins/cases/server/client/metrics/lifespan.test.ts +++ b/x-pack/plugins/cases/server/client/metrics/lifespan.test.ts @@ -6,7 +6,7 @@ */ import type { SavedObject } from '@kbn/core/server'; -import type { CaseUserActionInjectedAttributes } from '../../../common/api'; +import type { UserActionAttributes } from '../../../common/api'; import { CaseStatuses } from '../../../common/api'; import { getStatusInfo } from './lifespan'; import { createStatusChangeSavedObject } from './test_utils/lifespan'; @@ -120,7 +120,7 @@ describe('lifespan', () => { [ { attributes: { payload: { hello: 1, status: CaseStatuses.closed }, type: 'status' }, - } as unknown as SavedObject, + } as unknown as SavedObject, ], new Date(0) ); @@ -133,7 +133,7 @@ describe('lifespan', () => { [ { attributes: { payload: { status: CaseStatuses.closed }, type: 'awesome' }, - } as unknown as SavedObject, + } as unknown as SavedObject, ], new Date(0) ); @@ -149,7 +149,7 @@ describe('lifespan', () => { payload: { status: CaseStatuses.closed, created_at: 'blah' }, type: 'status', }, - } as unknown as SavedObject, + } as unknown as SavedObject, ], new Date(0) ); diff --git a/x-pack/plugins/cases/server/client/metrics/lifespan.ts b/x-pack/plugins/cases/server/client/metrics/lifespan.ts index a78e5397b7a13..7e564c99f7fbc 100644 --- a/x-pack/plugins/cases/server/client/metrics/lifespan.ts +++ b/x-pack/plugins/cases/server/client/metrics/lifespan.ts @@ -7,7 +7,7 @@ import type { SavedObject } from '@kbn/core/server'; import type { - CaseUserActionInjectedAttributes, + UserActionAttributes, SingleCaseMetricsResponse, StatusInfo, StatusUserAction, @@ -83,7 +83,7 @@ interface StatusCalculations { } export function getStatusInfo( - statusUserActions: Array>, + statusUserActions: Array>, caseOpenTimestamp: Date ): StatusInfo { const accStatusInfo = statusUserActions.reduce( @@ -138,7 +138,7 @@ export function getStatusInfo( } function isValidStatusChangeUserAction( - attributes: CaseUserActionInjectedAttributes, + attributes: UserActionAttributes, newStatusChangeTimestamp: Date ): attributes is UserActionWithResponse { return StatusUserActionRt.is(attributes) && isDateValid(newStatusChangeTimestamp); diff --git a/x-pack/plugins/cases/server/client/metrics/test_utils/lifespan.ts b/x-pack/plugins/cases/server/client/metrics/test_utils/lifespan.ts index 62593e39e7d5c..b54204003b8ee 100644 --- a/x-pack/plugins/cases/server/client/metrics/test_utils/lifespan.ts +++ b/x-pack/plugins/cases/server/client/metrics/test_utils/lifespan.ts @@ -6,12 +6,12 @@ */ import type { SavedObject } from '@kbn/core/server'; -import type { CaseStatuses, CaseUserActionInjectedAttributes } from '../../../../common/api'; +import type { CaseStatuses, UserActionAttributes } from '../../../../common/api'; export function createStatusChangeSavedObject( status: CaseStatuses, createdAt: Date -): SavedObject { +): SavedObject { return { references: [], id: '', diff --git a/x-pack/plugins/cases/server/client/typedoc_interfaces.ts b/x-pack/plugins/cases/server/client/typedoc_interfaces.ts index f67a1eff4631e..f0fe44d4d44da 100644 --- a/x-pack/plugins/cases/server/client/typedoc_interfaces.ts +++ b/x-pack/plugins/cases/server/client/typedoc_interfaces.ts @@ -25,7 +25,7 @@ import type { CasesFindResponse, CasesPatchRequest, Cases, - CaseUserActionsResponse, + UserActions, CommentsResponse, CasesBulkGetResponse, } from '../../common/api'; @@ -51,4 +51,4 @@ export interface ICasesConfigurePatch extends CasesConfigurePatch {} export interface ICommentsResponse extends CommentsResponse {} export interface IAllCommentsResponse extends AllCommentsResponse {} -export interface ICaseUserActionsResponse extends CaseUserActionsResponse {} +export interface ICaseUserActionsResponse extends UserActions {} diff --git a/x-pack/plugins/cases/server/client/user_actions/connectors.ts b/x-pack/plugins/cases/server/client/user_actions/connectors.ts index da949996d0fd7..79673389881d0 100644 --- a/x-pack/plugins/cases/server/client/user_actions/connectors.ts +++ b/x-pack/plugins/cases/server/client/user_actions/connectors.ts @@ -13,7 +13,7 @@ import type { SavedObject } from '@kbn/core-saved-objects-common/src/server_type import type { GetCaseConnectorsResponse, CaseConnector, - CaseUserActionInjectedAttributes, + UserActionAttributes, CaseExternalServiceBasic, GetCaseConnectorsPushDetails, } from '../../../common/api'; @@ -75,7 +75,7 @@ const checkConnectorsAuthorization = async ({ authorization, }: { connectors: CaseConnectorActivity[]; - latestUserAction?: SavedObject; + latestUserAction?: SavedObject; authorization: PublicMethodsOf; }) => { const entities: OwnerEntity[] = latestUserAction @@ -127,7 +127,7 @@ const getConnectorsInfo = async ({ }: { caseId: string; connectors: CaseConnectorActivity[]; - latestUserAction?: SavedObject; + latestUserAction?: SavedObject; actionsClient: PublicMethodsOf; userActionService: CaseUserActionService; logger: CasesClientArgs['logger']; @@ -224,7 +224,7 @@ const getPushDetails = (activity: CaseConnectorActivity[]) => { }; const getExternalServiceFromSavedObject = ( - savedObject: SavedObject | undefined + savedObject: SavedObject | undefined ): CaseExternalServiceBasic | undefined => { if (savedObject != null && isPushedUserAction(savedObject.attributes)) { return savedObject.attributes.payload.externalService; @@ -248,7 +248,7 @@ const isDateValid = (date: Date): boolean => { }; const getConnectorInfoFromSavedObject = ( - savedObject: SavedObject | undefined + savedObject: SavedObject | undefined ): CaseConnector | undefined => { if ( savedObject != null && @@ -268,7 +268,7 @@ const createConnectorInfoResult = ({ actionConnectors: ActionResult[]; connectors: CaseConnectorActivity[]; pushInfo: Map; - latestUserAction?: SavedObject; + latestUserAction?: SavedObject; }) => { const results: GetCaseConnectorsResponse = {}; const actionConnectorsMap = new Map( diff --git a/x-pack/plugins/cases/server/client/user_actions/utils.ts b/x-pack/plugins/cases/server/client/user_actions/utils.ts index 98df79b6e94ab..bc2deb2b2caf6 100644 --- a/x-pack/plugins/cases/server/client/user_actions/utils.ts +++ b/x-pack/plugins/cases/server/client/user_actions/utils.ts @@ -7,8 +7,8 @@ import type { SavedObjectsFindResponse } from '@kbn/core-saved-objects-api-server'; import type { - CaseUserActionInjectedAttributes, - CaseUserActionsResponse, + UserActionAttributes, + UserActions, CaseUserActionsDeprecatedResponse, CaseUserActionDeprecatedResponse, } from '../../../common/api'; @@ -20,6 +20,6 @@ export const extractAttributes = ( }; export const formatSavedObjects = ( - response: SavedObjectsFindResponse -): CaseUserActionsResponse => + response: SavedObjectsFindResponse +): UserActions => response.saved_objects.map((so) => ({ id: so.id, version: so.version ?? '', ...so.attributes })); diff --git a/x-pack/plugins/cases/server/common/types/user_actions.ts b/x-pack/plugins/cases/server/common/types/user_actions.ts index bc5366a2adaa8..14823f159a408 100644 --- a/x-pack/plugins/cases/server/common/types/user_actions.ts +++ b/x-pack/plugins/cases/server/common/types/user_actions.ts @@ -5,6 +5,8 @@ * 2.0. */ +import type { SavedObject } from '@kbn/core/server'; +import type { UserActionAttributes } from '../../../common/api'; import type { User } from './user'; interface UserActionCommonPersistedAttributes { @@ -18,3 +20,6 @@ export interface UserActionPersistedAttributes extends UserActionCommonPersisted type: string; payload: Record; } + +export type UserActionTransformedAttributes = UserActionAttributes; +export type UserActionSavedObjectTransformed = SavedObject; diff --git a/x-pack/plugins/cases/server/services/user_actions/audit_logger.ts b/x-pack/plugins/cases/server/services/user_actions/audit_logger.ts index af3599c11c67b..fc1d60684949d 100644 --- a/x-pack/plugins/cases/server/services/user_actions/audit_logger.ts +++ b/x-pack/plugins/cases/server/services/user_actions/audit_logger.ts @@ -8,7 +8,7 @@ import type { EcsEvent } from '@kbn/ecs'; import type { AuditLogger } from '@kbn/security-plugin/server'; import type { ArrayElement } from '@kbn/utility-types'; -import type { UserAction as Action } from '../../../common/api'; +import type { ActionCategory as Action } from '../../../common/api'; import type { EventDetails } from './types'; const actionsToEcsType: Record> = { diff --git a/x-pack/plugins/cases/server/services/user_actions/builders/assignees.ts b/x-pack/plugins/cases/server/services/user_actions/builders/assignees.ts index ffd695e62df37..9c21bf916758d 100644 --- a/x-pack/plugins/cases/server/services/user_actions/builders/assignees.ts +++ b/x-pack/plugins/cases/server/services/user_actions/builders/assignees.ts @@ -6,7 +6,7 @@ */ import { CASE_SAVED_OBJECT } from '../../../../common/constants'; -import type { UserAction } from '../../../../common/api'; +import type { ActionCategory } from '../../../../common/api'; import { ActionTypes, Actions } from '../../../../common/api'; import { UserActionBuilder } from '../abstract_builder'; import type { EventDetails, UserActionParameters, UserActionEvent } from '../types'; @@ -44,7 +44,7 @@ export class AssigneesUserActionBuilder extends UserActionBuilder { } } -const getVerbMessage = (action: UserAction, uids: string[]) => { +const getVerbMessage = (action: ActionCategory, uids: string[]) => { const uidText = `uids: [${uids}]`; switch (action) { diff --git a/x-pack/plugins/cases/server/services/user_actions/builders/audit_logger_utils.ts b/x-pack/plugins/cases/server/services/user_actions/builders/audit_logger_utils.ts index 4010c358ce601..396301a648e42 100644 --- a/x-pack/plugins/cases/server/services/user_actions/builders/audit_logger_utils.ts +++ b/x-pack/plugins/cases/server/services/user_actions/builders/audit_logger_utils.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { UserAction as Action } from '../../../../common/api'; +import type { ActionCategory as Action } from '../../../../common/api'; const actionsToVerbs: Record = { add: 'added', diff --git a/x-pack/plugins/cases/server/services/user_actions/builders/tags.ts b/x-pack/plugins/cases/server/services/user_actions/builders/tags.ts index 1a9066cbbf224..5dc4197185761 100644 --- a/x-pack/plugins/cases/server/services/user_actions/builders/tags.ts +++ b/x-pack/plugins/cases/server/services/user_actions/builders/tags.ts @@ -6,7 +6,7 @@ */ import { CASE_SAVED_OBJECT } from '../../../../common/constants'; -import type { UserAction } from '../../../../common/api'; +import type { ActionCategory } from '../../../../common/api'; import { ActionTypes, Actions } from '../../../../common/api'; import { UserActionBuilder } from '../abstract_builder'; import type { EventDetails, UserActionParameters, UserActionEvent } from '../types'; @@ -45,7 +45,7 @@ export class TagsUserActionBuilder extends UserActionBuilder { } } -const getPreposition = (action: UserAction): string => { +const getPreposition = (action: ActionCategory): string => { switch (action) { case Actions.add: return 'to'; diff --git a/x-pack/plugins/cases/server/services/user_actions/index.ts b/x-pack/plugins/cases/server/services/user_actions/index.ts index 167f0e123fb07..521cee600469b 100644 --- a/x-pack/plugins/cases/server/services/user_actions/index.ts +++ b/x-pack/plugins/cases/server/services/user_actions/index.ts @@ -5,14 +5,11 @@ * 2.0. */ -import type { SavedObject, SavedObjectsFindResponse, SavedObjectsRawDoc } from '@kbn/core/server'; +import type { SavedObjectsFindResponse, SavedObjectsRawDoc } from '@kbn/core/server'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { KueryNode } from '@kbn/es-query'; -import type { - CaseUserActionDeprecatedResponse, - CaseUserActionInjectedAttributes, -} from '../../../common/api'; +import type { CaseUserActionDeprecatedResponse } from '../../../common/api'; import { ActionTypes } from '../../../common/api'; import { CASE_SAVED_OBJECT, @@ -38,7 +35,10 @@ import { defaultSortField } from '../../common/utils'; import { UserActionPersister } from './operations/create'; import { UserActionFinder } from './operations/find'; import { transformToExternalModel, legacyTransformFindResponseToExternalModel } from './transform'; -import type { UserActionPersistedAttributes } from '../../common/types/user_actions'; +import type { + UserActionPersistedAttributes, + UserActionSavedObjectTransformed, +} from '../../common/types/user_actions'; export class CaseUserActionService { private readonly _creator: UserActionPersister; @@ -222,7 +222,7 @@ export class CaseUserActionService { public async getMostRecentUserAction( caseId: string - ): Promise | undefined> { + ): Promise { try { this.context.log.debug( `Attempting to retrieve the most recent user action for case id: ${caseId}` @@ -327,7 +327,7 @@ export class CaseUserActionService { rawFieldsDoc = createCase.mostRecent.hits.hits[0]; } - let fieldsDoc: SavedObject | undefined; + let fieldsDoc: UserActionSavedObjectTransformed | undefined; if (rawFieldsDoc != null) { const doc = this.context.savedObjectsSerializer.rawToSavedObject( @@ -368,9 +368,7 @@ export class CaseUserActionService { } } - private getTopHitsDoc( - topHits: TopHits - ): SavedObject | undefined { + private getTopHitsDoc(topHits: TopHits): UserActionSavedObjectTransformed | undefined { if (topHits.hits.hits.length > 0) { const rawPushDoc = topHits.hits.hits[0]; diff --git a/x-pack/plugins/cases/server/services/user_actions/operations/create.ts b/x-pack/plugins/cases/server/services/user_actions/operations/create.ts index 25096baddf479..d4b5c0d283630 100644 --- a/x-pack/plugins/cases/server/services/user_actions/operations/create.ts +++ b/x-pack/plugins/cases/server/services/user_actions/operations/create.ts @@ -15,7 +15,7 @@ import type { ActionTypeValues, CaseAssignees, CaseUserProfile, - UserAction as Action, + ActionCategory, } from '../../../../common/api'; import { Actions, ActionTypes } from '../../../../common/api'; import { BuilderFactory } from '../builder_factory'; @@ -184,7 +184,7 @@ export class UserActionPersister { }: { commonArgs: CommonUserActionArgs; actionType: ActionType; - action: Action; + action: ActionCategory; createPayload: CreatePayloadFunction; modifiedItems?: Item[] | null; }) { diff --git a/x-pack/plugins/cases/server/services/user_actions/operations/find.ts b/x-pack/plugins/cases/server/services/user_actions/operations/find.ts index 5ce99d41a5ed2..c1b3a49ed6507 100644 --- a/x-pack/plugins/cases/server/services/user_actions/operations/find.ts +++ b/x-pack/plugins/cases/server/services/user_actions/operations/find.ts @@ -8,14 +8,9 @@ import type { KueryNode } from '@kbn/es-query'; import { fromKueryExpression } from '@kbn/es-query'; import type { SavedObjectsFindResponse } from '@kbn/core-saved-objects-api-server'; -import type { SavedObject } from '@kbn/core-saved-objects-server'; import { DEFAULT_PAGE, DEFAULT_PER_PAGE } from '../../../routes/api'; import { defaultSortField } from '../../../common/utils'; -import type { - ActionTypeValues, - FindTypeField, - CaseUserActionInjectedAttributes, -} from '../../../../common/api'; +import type { ActionTypeValues, FindTypeField } from '../../../../common/api'; import { Actions, ActionTypes, CommentType } from '../../../../common/api'; import { CASE_SAVED_OBJECT, @@ -26,7 +21,11 @@ import { import type { FindOptions, ServiceContext } from '../types'; import { transformFindResponseToExternalModel, transformToExternalModel } from '../transform'; import { buildFilter, combineFilters, NodeBuilderOperators } from '../../../client/utils'; -import type { UserActionPersistedAttributes } from '../../../common/types/user_actions'; +import type { + UserActionPersistedAttributes, + UserActionSavedObjectTransformed, + UserActionTransformedAttributes, +} from '../../../common/types/user_actions'; export class UserActionFinder { constructor(private readonly context: ServiceContext) {} @@ -38,7 +37,7 @@ export class UserActionFinder { page, perPage, filter, - }: FindOptions): Promise> { + }: FindOptions): Promise> { try { this.context.log.debug(`Attempting to find user actions for case id: ${caseId}`); @@ -168,7 +167,7 @@ export class UserActionFinder { }: { caseId: string; filter?: KueryNode; - }): Promise>> { + }): Promise { try { this.context.log.debug('Attempting to find status changes'); @@ -200,7 +199,7 @@ export class UserActionFinder { } ); - let userActions: Array> = []; + let userActions: UserActionSavedObjectTransformed[] = []; for await (const findResults of finder.find()) { userActions = userActions.concat( findResults.saved_objects.map((so) => diff --git a/x-pack/plugins/cases/server/services/user_actions/test_utils.ts b/x-pack/plugins/cases/server/services/user_actions/test_utils.ts index 49d47b13f8d04..b4dbb114fc2fe 100644 --- a/x-pack/plugins/cases/server/services/user_actions/test_utils.ts +++ b/x-pack/plugins/cases/server/services/user_actions/test_utils.ts @@ -21,7 +21,7 @@ import { import type { CaseUserActionAttributesWithoutConnectorId, ConnectorUserAction, - UserAction, + ActionCategory, } from '../../../common/api'; import { CaseSeverity, CaseStatuses, Actions } from '../../../common/api'; import { @@ -76,7 +76,7 @@ export const createUserActionSO = ({ type, references = [], }: { - action: UserAction; + action: ActionCategory; type?: string; payload?: Record; attributesOverrides?: Partial; diff --git a/x-pack/plugins/cases/server/services/user_actions/transform.ts b/x-pack/plugins/cases/server/services/user_actions/transform.ts index 10a6104e15807..fb0a17fc6a57b 100644 --- a/x-pack/plugins/cases/server/services/user_actions/transform.ts +++ b/x-pack/plugins/cases/server/services/user_actions/transform.ts @@ -14,11 +14,7 @@ import { isCreateCaseUserAction, isCommentUserAction, } from '../../../common/utils/user_actions'; -import type { - CaseUserActionAttributes, - CaseUserActionDeprecatedResponse, - CaseUserActionInjectedAttributes, -} from '../../../common/api'; +import type { CaseUserActionDeprecatedResponse, UserActionAttributes } from '../../../common/api'; import { NONE_CONNECTOR_ID } from '../../../common/api'; import { CASE_SAVED_OBJECT, CASE_COMMENT_SAVED_OBJECT } from '../../../common/constants'; import { @@ -33,12 +29,16 @@ import { isCommentRequestTypeExternalReferenceSO } from '../type_guards'; import type { PersistableStateAttachmentTypeRegistry } from '../../attachment_framework/persistable_state_registry'; import { injectPersistableReferencesToSO } from '../../attachment_framework/so_references'; import { findReferenceId } from '../../common/references'; -import type { UserActionPersistedAttributes } from '../../common/types/user_actions'; +import type { + UserActionPersistedAttributes, + UserActionSavedObjectTransformed, + UserActionTransformedAttributes, +} from '../../common/types/user_actions'; export function transformFindResponseToExternalModel( userActions: SavedObjectsFindResponse, persistableStateAttachmentTypeRegistry: PersistableStateAttachmentTypeRegistry -): SavedObjectsFindResponse { +): SavedObjectsFindResponse { return { ...userActions, saved_objects: userActions.saved_objects.map((so) => ({ @@ -51,7 +51,7 @@ export function transformFindResponseToExternalModel( export function transformToExternalModel( userAction: SavedObject, persistableStateAttachmentTypeRegistry: PersistableStateAttachmentTypeRegistry -): SavedObject { +): UserActionSavedObjectTransformed { const { references } = userAction; const commentId = @@ -64,7 +64,7 @@ export function transformToExternalModel( ...userAction.attributes, comment_id: commentId, payload, - } as CaseUserActionInjectedAttributes, + } as UserActionTransformedAttributes, }; } @@ -116,7 +116,7 @@ function legacyTransformToExternalModel( const addReferenceIdToPayload = ( userAction: SavedObject, persistableStateAttachmentTypeRegistry: PersistableStateAttachmentTypeRegistry -): CaseUserActionAttributes['payload'] => { +): UserActionAttributes['payload'] => { const connectorId = getConnectorIdFromReferences(userAction); const userActionAttributes = userAction.attributes; diff --git a/x-pack/plugins/cases/server/services/user_actions/types.ts b/x-pack/plugins/cases/server/services/user_actions/types.ts index f88bd2ba3fe15..addea6ddfa763 100644 --- a/x-pack/plugins/cases/server/services/user_actions/types.ts +++ b/x-pack/plugins/cases/server/services/user_actions/types.ts @@ -10,7 +10,6 @@ import type { SavedObjectsClientContract, Logger, ISavedObjectsSerializer, - SavedObject, SavedObjectsRawDoc, SavedObjectsUpdateResponse, } from '@kbn/core/server'; @@ -25,18 +24,20 @@ import type { CaseSeverity, CaseStatuses, CaseUserActionAttributesWithoutConnectorId, - CaseUserActionInjectedAttributes, CommentRequest, CommentUserAction, ConnectorUserAction, PushedUserAction, User, - UserAction, + ActionCategory, UserActionFindRequest, UserActionTypes, } from '../../../common/api'; import type { PersistableStateAttachmentTypeRegistry } from '../../attachment_framework/persistable_state_registry'; -import type { UserActionPersistedAttributes } from '../../common/types/user_actions'; +import type { + UserActionPersistedAttributes, + UserActionSavedObjectTransformed, +} from '../../common/types/user_actions'; import type { IndexRefresh } from '../types'; import type { CaseSavedObjectTransformed } from '../../common/types/case'; @@ -105,7 +106,16 @@ export interface CommonArguments { owner: string; attachmentId?: string; connectorId?: string; - action?: UserAction; + action?: ActionCategory; +} + +export interface Attributes { + action: ActionCategory; + created_at: string; + created_by: User; + owner: string; + type: UserActionTypes; + payload: Record; } export interface SavedObjectParameters { @@ -115,7 +125,7 @@ export interface SavedObjectParameters { export interface EventDetails { getMessage: (storedUserActionId?: string) => string; - action: UserAction; + action: ActionCategory; descriptiveAction: string; savedObjectId: string; savedObjectType: string; @@ -127,7 +137,7 @@ export interface UserActionEvent { } export type CommonBuilderArguments = CommonArguments & { - action: UserAction; + action: ActionCategory; type: UserActionTypes; value: unknown; valueKey: string; @@ -146,17 +156,17 @@ export interface ServiceContext { } export interface PushTimeFrameInfo { - mostRecent: SavedObject; - oldest: SavedObject; + mostRecent: UserActionSavedObjectTransformed; + oldest: UserActionSavedObjectTransformed; } export interface CaseConnectorActivity { connectorId: string; - fields: SavedObject; + fields: UserActionSavedObjectTransformed; push?: PushTimeFrameInfo; } -export type CaseConnectorFields = Map>; +export type CaseConnectorFields = Map; export interface PushInfo { date: Date; diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explorer/analytics_collection_explorer_table.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explorer/analytics_collection_explorer_table.tsx index 35cf7afbd1243..19457083db2cc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explorer/analytics_collection_explorer_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explorer/analytics_collection_explorer_table.tsx @@ -16,6 +16,7 @@ import { EuiFieldSearch, EuiFlexGroup, EuiHorizontalRule, + EuiI18nNumber, EuiSpacer, EuiTab, EuiTabs, @@ -270,6 +271,7 @@ const tableSettings: { }, }, }; +const MAX_ITEMS = 10000; export const AnalyticsCollectionExplorerTable = () => { const { euiTheme } = useEuiTheme(); @@ -292,6 +294,8 @@ export const AnalyticsCollectionExplorerTable = () => { const handleTableChange = ({ sort, page }: Criteria) => { onTableChange({ page, sort }); }; + const startNumberItemsOnPage = pageSize * pageIndex + (items.length ? 1 : 0); + const endNumberItemsOnPage = pageSize * pageIndex + items.length; useEffect(() => { if (!selectedTable) { @@ -335,19 +339,33 @@ export const AnalyticsCollectionExplorerTable = () => { - - {pageSize * pageIndex + Number(!!items.length)}- - {pageSize * pageIndex + items.length} - - ), - totalItemsCount, - }} - /> + {totalItemsCount > MAX_ITEMS ? ( + + {startNumberItemsOnPage}-{endNumberItemsOnPage} + + ), + maxItemsCount: , + }} + /> + ) : ( + + {startNumberItemsOnPage}-{endNumberItemsOnPage} + + ), + totalItemsCount, + }} + /> + )} @@ -365,7 +383,7 @@ export const AnalyticsCollectionExplorerTable = () => { pageSize, pageSizeOptions: [10, 20, 50], showPerPageOptions: true, - totalItemCount: totalItemsCount, + totalItemCount: Math.min(totalItemsCount, MAX_ITEMS), }} onChange={handleTableChange} /> diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/text_expansion/create_text_expansion_model_api_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/text_expansion/create_text_expansion_model_api_logic.ts index 315815a02ef08..643352e0a833e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/text_expansion/create_text_expansion_model_api_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/text_expansion/create_text_expansion_model_api_logic.ts @@ -24,7 +24,8 @@ export const createTextExpansionModel = async (): Promise { export const FetchTextExpansionModelApiLogic = createApiLogic( ['fetch_text_expansion_model_api_logic'], - fetchTextExpansionModelStatus + fetchTextExpansionModelStatus, + { showErrorFlash: false } ); export type FetchTextExpansionModelApiLogicActions = Actions< diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/text_expansion/start_text_expansion_model_api_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/text_expansion/start_text_expansion_model_api_logic.ts index fa925be4bce00..d8e1116154b64 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/text_expansion/start_text_expansion_model_api_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/ml_models/text_expansion/start_text_expansion_model_api_logic.ts @@ -24,7 +24,8 @@ export const startTextExpansionModel = async (): Promise { const { engineName } = useValues(EngineViewLogic); const { isGenerateModalOpen } = useValues(EngineApiLogic); const { openGenerateModal, closeGenerateModal } = useActions(EngineApiLogic); - const { navigateToUrl } = useValues(KibanaLogic); const cloudContext = useCloudDetails(); const steps = [ @@ -153,48 +149,6 @@ export const SearchApplicationAPI = () => { }), children: , }, - { - title: i18n.translate('xpack.enterpriseSearch.content.searchApplication.api.step4.title', { - defaultMessage: '(Optional) Power up your analytics', - }), - children: ( - <> - -

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

-
- - - - - navigateToUrl( - generateEncodedPath(`${ANALYTICS_PLUGIN.URL}${COLLECTION_INTEGRATE_PATH}`, { - id: engineName, - }), - { shouldNotCreateHref: true } - ) - } - iconSide="left" - iconType="popout" - > - {i18n.translate( - 'xpack.enterpriseSearch.content.searchApplication.api.step4.learnHowLink', - { - defaultMessage: 'Learn how', - } - )} - - - - - ), - }, ]; return ( diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx index 35624797f0488..7c2815eab946e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration.tsx @@ -99,28 +99,10 @@ export const ConnectorConfiguration: React.FC = () => { { children: ( <> - - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.description.firstParagraph', - { - defaultMessage: - 'The connectors repository contains several connector client examples to help you utilize our framework for accelerated development against custom data sources.', - } - )} - - - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.button.label', - { - defaultMessage: 'Explore the connectors repository', - } - )} - - { 'xpack.enterpriseSearch.content.indices.configurationConnector.scheduleSync.description', { defaultMessage: - 'Once your connectors are configured to your liking, don’t forget to set a recurring sync schedule to make sure your documents are indexed and relevant. You can also trigger a one-time sync without enabling a sync schedule.', + 'Once configured, set a recurring sync schedule to keep your documents in sync over time. You can also simply trigger a one-time sync.', } )} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout.test.tsx index 2ba6da3b4843f..9480f4b1a0ce5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout.test.tsx @@ -13,6 +13,8 @@ import { shallow } from 'enzyme'; import { EuiButton } from '@elastic/eui'; +import { HttpError } from '../../../../../../../common/types/api'; + import { TextExpansionCallOut, DeployModel, @@ -22,6 +24,8 @@ import { ModelStarted, } from './text_expansion_callout'; +import { TextExpansionErrors } from './text_expansion_errors'; + jest.mock('./text_expansion_callout_data', () => ({ useTextExpansionCallOutData: jest.fn(() => ({ dismiss: jest.fn(), @@ -33,6 +37,7 @@ jest.mock('./text_expansion_callout_data', () => ({ })); const DEFAULT_VALUES = { + startTextExpansionModelError: undefined, isCreateButtonDisabled: false, isModelDownloadInProgress: false, isModelDownloaded: false, @@ -45,6 +50,21 @@ describe('TextExpansionCallOut', () => { jest.clearAllMocks(); setMockValues(DEFAULT_VALUES); }); + it('renders error panel instead of normal panel if there are some errors', () => { + setMockValues({ + ...DEFAULT_VALUES, + startTextExpansionModelError: { + body: { + error: 'some-error', + message: 'some-error-message', + statusCode: 500, + }, + } as HttpError, + }); + + const wrapper = shallow(); + expect(wrapper.find(TextExpansionErrors).length).toBe(1); + }); it('renders panel with deployment instructions if the model is not deployed', () => { const wrapper = shallow(); expect(wrapper.find(DeployModel).length).toBe(1); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout.tsx index 94a4d4d05fc96..a72a328188c6f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout.tsx @@ -29,7 +29,8 @@ import { docLinks } from '../../../../../shared/doc_links'; import { KibanaLogic } from '../../../../../shared/kibana'; import { useTextExpansionCallOutData } from './text_expansion_callout_data'; -import { TextExpansionCalloutLogic } from './text_expansion_callout_logic'; +import { getTextExpansionError, TextExpansionCalloutLogic } from './text_expansion_callout_logic'; +import { TextExpansionErrors } from './text_expansion_errors'; export interface TextExpansionCallOutState { dismiss: () => void; @@ -327,13 +328,24 @@ export const ModelStarted = ({ export const TextExpansionCallOut: React.FC = (props) => { const { dismiss, isDismissable, show } = useTextExpansionCallOutData(props); const { + createTextExpansionModelError, + fetchTextExpansionModelError, isCreateButtonDisabled, isModelDownloadInProgress, isModelDownloaded, isModelStarted, isStartButtonDisabled, + startTextExpansionModelError, } = useValues(TextExpansionCalloutLogic); + // In case of an error, show the error callout only + const error = getTextExpansionError( + createTextExpansionModelError, + fetchTextExpansionModelError, + startTextExpansionModelError + ); + if (error) return ; + if (!show) return null; if (isModelDownloadInProgress) { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout_logic.test.ts index 099e6b31e2b6a..ae781bfb3bd1d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout_logic.test.ts @@ -9,25 +9,29 @@ import { LogicMounter } from '../../../../../__mocks__/kea_logic'; import { HttpResponse } from '@kbn/core/public'; -import { ErrorResponse, Status } from '../../../../../../../common/types/api'; +import { ErrorResponse, HttpError, Status } from '../../../../../../../common/types/api'; import { MlModelDeploymentState } from '../../../../../../../common/types/ml'; import { CreateTextExpansionModelApiLogic } from '../../../../api/ml_models/text_expansion/create_text_expansion_model_api_logic'; import { FetchTextExpansionModelApiLogic } from '../../../../api/ml_models/text_expansion/fetch_text_expansion_model_api_logic'; import { + getTextExpansionError, TextExpansionCalloutLogic, TextExpansionCalloutValues, } from './text_expansion_callout_logic'; const DEFAULT_VALUES: TextExpansionCalloutValues = { + createTextExpansionModelError: undefined, createTextExpansionModelStatus: Status.IDLE, createdTextExpansionModel: undefined, + fetchTextExpansionModelError: undefined, isCreateButtonDisabled: false, isModelDownloadInProgress: false, isModelDownloaded: false, isModelStarted: false, isPollingTextExpansionModelActive: false, isStartButtonDisabled: false, + startTextExpansionModelError: undefined, startTextExpansionModelStatus: Status.IDLE, textExpansionModel: undefined, textExpansionModelPollTimeoutId: null, @@ -55,6 +59,37 @@ describe('TextExpansionCalloutLogic', () => { expect(TextExpansionCalloutLogic.values).toEqual(DEFAULT_VALUES); }); + describe('getTextExpansionError', () => { + const error = { + body: { + error: 'some-error', + message: 'some-error-message', + statusCode: 500, + }, + } as HttpError; + it('returns null if there is no error', () => { + expect(getTextExpansionError(undefined, undefined, undefined)).toBe(null); + }); + it('uses the correct title and message from a create error', () => { + expect(getTextExpansionError(error, undefined, undefined)).toEqual({ + title: 'Error with ELSER deployment', + message: error.body?.message, + }); + }); + it('uses the correct title and message from a fetch error', () => { + expect(getTextExpansionError(undefined, error, undefined)).toEqual({ + title: 'Error fetching ELSER model', + message: error.body?.message, + }); + }); + it('uses the correct title and message from a start error', () => { + expect(getTextExpansionError(undefined, undefined, error)).toEqual({ + title: 'Error starting ELSER deployment', + message: error.body?.message, + }); + }); + }); + describe('listeners', () => { describe('createTextExpansionModelPollingTimeout', () => { const duration = 5000; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout_logic.ts index 7d301e1bdf59d..dda1be828ee0a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_callout_logic.ts @@ -7,8 +7,11 @@ import { kea, MakeLogicType } from 'kea'; -import { Status } from '../../../../../../../common/types/api'; +import { i18n } from '@kbn/i18n'; + +import { HttpError, Status } from '../../../../../../../common/types/api'; import { MlModelDeploymentState } from '../../../../../../../common/types/ml'; +import { getErrorsFromHttpResponse } from '../../../../../shared/flash_messages/handle_api_errors'; import { CreateTextExpansionModelApiLogic, CreateTextExpansionModelApiLogicActions, @@ -46,19 +49,67 @@ interface TextExpansionCalloutActions { } export interface TextExpansionCalloutValues { + createTextExpansionModelError: HttpError | undefined; createTextExpansionModelStatus: Status; createdTextExpansionModel: CreateTextExpansionModelResponse | undefined; + fetchTextExpansionModelError: HttpError | undefined; isCreateButtonDisabled: boolean; isModelDownloadInProgress: boolean; isModelDownloaded: boolean; isModelStarted: boolean; isPollingTextExpansionModelActive: boolean; isStartButtonDisabled: boolean; + startTextExpansionModelError: HttpError | undefined; startTextExpansionModelStatus: Status; textExpansionModel: FetchTextExpansionModelResponse | undefined; textExpansionModelPollTimeoutId: null | ReturnType; } +/** + * Extracts the topmost error in precedence order (create > start > fetch). + * @param createError + * @param fetchError + * @param startError + * @returns the extracted error or null if there is no error + */ +export const getTextExpansionError = ( + createError: HttpError | undefined, + fetchError: HttpError | undefined, + startError: HttpError | undefined +) => { + return createError !== undefined + ? { + title: i18n.translate( + 'xpack.enterpriseSearch.content.indices.pipelines.textExpansionCreateError.title', + { + defaultMessage: 'Error with ELSER deployment', + } + ), + message: getErrorsFromHttpResponse(createError)[0], + } + : startError !== undefined + ? { + title: i18n.translate( + 'xpack.enterpriseSearch.content.indices.pipelines.textExpansionStartError.title', + { + defaultMessage: 'Error starting ELSER deployment', + } + ), + message: getErrorsFromHttpResponse(startError)[0], + } + : fetchError !== undefined + ? { + title: i18n.translate( + 'xpack.enterpriseSearch.content.indices.pipelines.textExpansionFetchError.title', + { + defaultMessage: 'Error fetching ELSER model', + } + ), + message: getErrorsFromHttpResponse(fetchError)[0], + } + : null; +}; + export const TextExpansionCalloutLogic = kea< MakeLogicType >({ @@ -74,7 +125,11 @@ export const TextExpansionCalloutLogic = kea< connect: { actions: [ CreateTextExpansionModelApiLogic, - ['makeRequest as createTextExpansionModel', 'apiSuccess as createTextExpansionModelSuccess'], + [ + 'makeRequest as createTextExpansionModel', + 'apiSuccess as createTextExpansionModelSuccess', + 'apiError as createTextExpansionModelError', + ], FetchTextExpansionModelApiLogic, [ 'makeRequest as fetchTextExpansionModel', @@ -82,15 +137,23 @@ export const TextExpansionCalloutLogic = kea< 'apiError as fetchTextExpansionModelError', ], StartTextExpansionModelApiLogic, - ['makeRequest as startTextExpansionModel', 'apiSuccess as startTextExpansionModelSuccess'], + [ + 'makeRequest as startTextExpansionModel', + 'apiSuccess as startTextExpansionModelSuccess', + 'apiError as startTextExpansionModelError', + ], ], values: [ CreateTextExpansionModelApiLogic, - ['data as createdTextExpansionModel', 'status as createTextExpansionModelStatus'], + [ + 'data as createdTextExpansionModel', + 'status as createTextExpansionModelStatus', + 'error as createTextExpansionModelError', + ], FetchTextExpansionModelApiLogic, - ['data as textExpansionModel'], + ['data as textExpansionModel', 'error as fetchTextExpansionModelError'], StartTextExpansionModelApiLogic, - ['status as startTextExpansionModelStatus'], + ['status as startTextExpansionModelStatus', 'error as startTextExpansionModelError'], ], }, events: ({ actions, values }) => ({ @@ -169,7 +232,7 @@ export const TextExpansionCalloutLogic = kea< selectors: ({ selectors }) => ({ isCreateButtonDisabled: [ () => [selectors.createTextExpansionModelStatus], - (status: Status) => status !== Status.IDLE, + (status: Status) => status !== Status.IDLE && status !== Status.ERROR, ], isModelDownloadInProgress: [ () => [selectors.textExpansionModel], @@ -195,7 +258,7 @@ export const TextExpansionCalloutLogic = kea< ], isStartButtonDisabled: [ () => [selectors.startTextExpansionModelStatus], - (status: Status) => status !== Status.IDLE, + (status: Status) => status !== Status.IDLE && status !== Status.ERROR, ], }), }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_errors.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_errors.test.tsx new file mode 100644 index 0000000000000..cb15c2c5c5411 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_errors.test.tsx @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { setMockValues } from '../../../../../__mocks__/kea_logic'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { EuiCallOut } from '@elastic/eui'; + +import { TextExpansionErrors } from './text_expansion_errors'; + +describe('TextExpansionErrors', () => { + beforeEach(() => { + jest.clearAllMocks(); + setMockValues({}); + }); + const error = { + title: 'some-error-title', + message: 'some-error-message', + }; + it('extracts error panel with the given title and message', () => { + const wrapper = shallow(); + expect(wrapper.find(EuiCallOut).length).toBe(1); + expect(wrapper.find(EuiCallOut).prop('title')).toEqual(error.title); + expect(wrapper.find(EuiCallOut).find('p').length).toBe(1); + expect(wrapper.find(EuiCallOut).find('p').text()).toEqual(error.message); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_errors.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_errors.tsx new file mode 100644 index 0000000000000..d9b1d1f1f2bc2 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/text_expansion_errors.tsx @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { useValues } from 'kea'; + +import { EuiCallOut, EuiLink } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { HttpLogic } from '../../../../../shared/http'; + +import { ML_NOTIFICATIONS_PATH } from '../../../../routes'; + +export const TextExpansionErrors = ({ error }: { error: { title: string; message: string } }) => { + const { http } = useValues(HttpLogic); + + return ( + <> + +

{error.message}

+ + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.pipelines.textExpansionCreateError.mlNotificationsLink', + { + defaultMessage: 'Machine Learning notifications', + } + )} + +
+ + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/routes.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/routes.ts index 7b5bc7bf286f5..cfd2447b9aa8b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/routes.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/routes.ts @@ -46,3 +46,4 @@ export enum SearchApplicationContentTabs { } export const ML_MANAGE_TRAINED_MODELS_PATH = '/app/ml/trained_models'; +export const ML_NOTIFICATIONS_PATH = '/app/ml/notifications'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/handle_api_errors.ts b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/handle_api_errors.ts index bb0f9c9253771..9e74d90605dc0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/handle_api_errors.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/handle_api_errors.ts @@ -33,7 +33,7 @@ interface Options { isQueued?: boolean; } -export const defaultErrorMessage = i18n.translate( +export const defaultErrorMessage: string = i18n.translate( 'xpack.enterpriseSearch.shared.flashMessages.defaultErrorMessage', { defaultMessage: 'An unexpected error occurred', diff --git a/x-pack/plugins/enterprise_search/server/lib/engines/fetch_indices_stats.test.ts b/x-pack/plugins/enterprise_search/server/lib/engines/fetch_indices_stats.test.ts index 3282fa37f4f4d..55905e40c6835 100644 --- a/x-pack/plugins/enterprise_search/server/lib/engines/fetch_indices_stats.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/engines/fetch_indices_stats.test.ts @@ -22,11 +22,16 @@ describe('fetchIndicesStats lib function', () => { indices: { 'test-index-name-1': { health: 'GREEN', - primaries: { docs: [{}] }, + primaries: { + docs: { + count: 200, + deleted: 0, + }, + }, status: 'open', total: { docs: { - count: 200, + count: 400, deleted: 0, }, }, @@ -34,7 +39,12 @@ describe('fetchIndicesStats lib function', () => { }, 'test-index-name-2': { health: 'YELLOW', - primaries: { docs: [{}] }, + primaries: { + docs: { + count: 0, + deleted: 0, + }, + }, status: 'closed', total: { docs: { @@ -46,11 +56,16 @@ describe('fetchIndicesStats lib function', () => { }, 'test-index-name-3': { health: 'RED', - primaries: { docs: [{}] }, + primaries: { + docs: { + count: 150, + deleted: 0, + }, + }, status: 'open', total: { docs: { - count: 150, + count: 300, deleted: 0, }, }, diff --git a/x-pack/plugins/enterprise_search/server/lib/engines/fetch_indices_stats.ts b/x-pack/plugins/enterprise_search/server/lib/engines/fetch_indices_stats.ts index c682309bbae4a..234fab310cca3 100644 --- a/x-pack/plugins/enterprise_search/server/lib/engines/fetch_indices_stats.ts +++ b/x-pack/plugins/enterprise_search/server/lib/engines/fetch_indices_stats.ts @@ -18,7 +18,7 @@ export const fetchIndicesStats = async (client: IScopedClusterClient, indices: s const indicesWithStats = indices.map((indexName: string) => { const indexStats = indicesStats[indexName]; const hydratedIndex: EnterpriseSearchEngineIndex = { - count: indexStats?.total?.docs?.count ?? 0, + count: indexStats?.primaries?.docs?.count ?? 0, health: indexStats?.health ?? 'unknown', name: indexName, }; diff --git a/x-pack/plugins/event_log/server/plugin.ts b/x-pack/plugins/event_log/server/plugin.ts index d5bf14a1259c1..9c96e2ae8073a 100644 --- a/x-pack/plugins/event_log/server/plugin.ts +++ b/x-pack/plugins/event_log/server/plugin.ts @@ -12,23 +12,19 @@ import { Plugin as CorePlugin, PluginInitializerContext, IClusterClient, - IContextProvider, } from '@kbn/core/server'; import { SpacesPluginStart } from '@kbn/spaces-plugin/server'; import type { - EventLogRequestHandlerContext, IEventLogConfig, IEventLogService, IEventLogger, IEventLogClientService, } from './types'; -import { findRoute } from './routes'; import { EventLogService } from './event_log_service'; import { createEsContext, EsContext } from './es'; import { EventLogClientService } from './event_log_start_service'; import { SavedObjectProviderRegistry } from './saved_object_provider_registry'; -import { findByIdsRoute } from './routes/find_by_ids'; export type PluginClusterClient = Pick; @@ -88,17 +84,6 @@ export class Plugin implements CorePlugin( - 'eventLog', - this.createRouteHandlerContext() - ); - - // Routes - const router = core.http.createRouter(); - // Register routes - findRoute(router, this.systemLogger); - findByIdsRoute(router, this.systemLogger); - return this.eventLogService; } @@ -161,15 +146,4 @@ export class Plugin implements CorePlugin => { - return async (context, request) => { - return { - getEventLogClient: () => this.eventLogClientService!.getClient(request), - }; - }; - }; } diff --git a/x-pack/plugins/event_log/server/routes/_mock_handler_arguments.ts b/x-pack/plugins/event_log/server/routes/_mock_handler_arguments.ts deleted file mode 100644 index d9c78aea08b8e..0000000000000 --- a/x-pack/plugins/event_log/server/routes/_mock_handler_arguments.ts +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { identity, merge } from 'lodash'; -import { RequestHandlerContext, KibanaRequest, KibanaResponseFactory } from '@kbn/core/server'; -import type { MethodKeysOf } from '@kbn/utility-types'; - -import { httpServerMock } from '@kbn/core/server/mocks'; -import { IEventLogClient } from '../types'; - -export function mockHandlerArguments( - eventLogClient: IEventLogClient, - request: unknown, - response?: Array> -): [RequestHandlerContext, KibanaRequest, KibanaResponseFactory] { - return [ - { - eventLog: { - getEventLogClient() { - return eventLogClient; - }, - }, - } as unknown as RequestHandlerContext, - request as KibanaRequest, - mockResponseFactory(response), - ]; -} - -export const mockResponseFactory = (resToMock: Array> = []) => { - const factory: jest.Mocked = httpServerMock.createResponseFactory(); - resToMock.forEach((key: string) => { - if (key in factory) { - Object.defineProperty(factory, key, { - value: jest.fn(identity), - }); - } - }); - return factory as unknown as KibanaResponseFactory; -}; - -export function fakeEvent(overrides = {}) { - return merge( - { - event: { - provider: 'actions', - action: 'execute', - start: '2020-03-30T14:55:47.054Z', - end: '2020-03-30T14:55:47.055Z', - duration: '1000000', - }, - kibana: { - saved_objects: [ - { - namespace: 'default', - type: 'action', - id: '968f1b82-0414-4a10-becc-56b6473e4a29', - }, - ], - server_uuid: '5b2de169-2785-441b-ae8c-186a1936b17d', - }, - message: 'action executed: .server-log:968f1b82-0414-4a10-becc-56b6473e4a29: logger', - '@timestamp': '2020-03-30T14:55:47.055Z', - ecs: { - version: '1.3.1', - }, - }, - overrides - ); -} diff --git a/x-pack/plugins/event_log/server/routes/find.test.ts b/x-pack/plugins/event_log/server/routes/find.test.ts deleted file mode 100644 index ee7985d34d249..0000000000000 --- a/x-pack/plugins/event_log/server/routes/find.test.ts +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { findRoute } from './find'; -import { httpServiceMock } from '@kbn/core/server/mocks'; -import { mockHandlerArguments, fakeEvent } from './_mock_handler_arguments'; -import { eventLogClientMock } from '../event_log_client.mock'; -import { loggingSystemMock } from '@kbn/core/server/mocks'; - -const eventLogClient = eventLogClientMock.create(); -const systemLogger = loggingSystemMock.createLogger(); - -beforeEach(() => { - jest.resetAllMocks(); -}); - -describe('find', () => { - it('finds events with proper parameters', async () => { - const router = httpServiceMock.createRouter(); - - findRoute(router, systemLogger); - - const [config, handler] = router.get.mock.calls[0]; - - expect(config.path).toMatchInlineSnapshot(`"/internal/event_log/{type}/{id}/_find"`); - - const events = [fakeEvent(), fakeEvent()]; - const result = { - page: 0, - per_page: 10, - total: events.length, - data: events, - }; - eventLogClient.findEventsBySavedObjectIds.mockResolvedValueOnce(result); - - const [context, req, res] = mockHandlerArguments( - eventLogClient, - { - params: { id: '1', type: 'action' }, - }, - ['ok'] - ); - - await handler(context, req, res); - - expect(eventLogClient.findEventsBySavedObjectIds).toHaveBeenCalledTimes(1); - - const [type, ids] = eventLogClient.findEventsBySavedObjectIds.mock.calls[0]; - expect(type).toEqual(`action`); - expect(ids).toEqual(['1']); - - expect(res.ok).toHaveBeenCalledWith({ - body: result, - }); - }); - - it('supports optional pagination parameters', async () => { - const router = httpServiceMock.createRouter(); - - findRoute(router, systemLogger); - - const [, handler] = router.get.mock.calls[0]; - eventLogClient.findEventsBySavedObjectIds.mockResolvedValueOnce({ - page: 0, - per_page: 10, - total: 0, - data: [], - }); - - const [context, req, res] = mockHandlerArguments( - eventLogClient, - { - params: { id: '1', type: 'action' }, - query: { page: 3, per_page: 10 }, - }, - ['ok'] - ); - - await handler(context, req, res); - - expect(eventLogClient.findEventsBySavedObjectIds).toHaveBeenCalledTimes(1); - - const [type, ids, options] = eventLogClient.findEventsBySavedObjectIds.mock.calls[0]; - expect(type).toEqual(`action`); - expect(ids).toEqual(['1']); - expect(options).toMatchObject({}); - - expect(res.ok).toHaveBeenCalledWith({ - body: { - page: 0, - per_page: 10, - total: 0, - data: [], - }, - }); - }); - - it('logs a warning when the query throws an error', async () => { - const router = httpServiceMock.createRouter(); - - findRoute(router, systemLogger); - - const [, handler] = router.get.mock.calls[0]; - eventLogClient.findEventsBySavedObjectIds.mockRejectedValueOnce(new Error('oof!')); - - const [context, req, res] = mockHandlerArguments( - eventLogClient, - { - params: { id: '1', type: 'action' }, - query: { page: 3, per_page: 10 }, - }, - ['ok'] - ); - - await handler(context, req, res); - - expect(systemLogger.debug).toHaveBeenCalledTimes(1); - expect(systemLogger.debug).toHaveBeenCalledWith( - 'error calling eventLog findEventsBySavedObjectIds(action, [1], {"page":3,"per_page":10}): oof!' - ); - }); -}); diff --git a/x-pack/plugins/event_log/server/routes/find.ts b/x-pack/plugins/event_log/server/routes/find.ts deleted file mode 100644 index 4c9ab70bbe3a5..0000000000000 --- a/x-pack/plugins/event_log/server/routes/find.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { schema, TypeOf } from '@kbn/config-schema'; -import type { - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, - Logger, -} from '@kbn/core/server'; -import type { EventLogRouter, EventLogRequestHandlerContext } from '../types'; -import { BASE_EVENT_LOG_API_PATH } from '../../common'; -import { queryOptionsSchema, FindOptionsType } from '../event_log_client'; - -const paramSchema = schema.object({ - type: schema.string(), - id: schema.string(), -}); - -export const findRoute = (router: EventLogRouter, systemLogger: Logger) => { - router.get( - { - path: `${BASE_EVENT_LOG_API_PATH}/{type}/{id}/_find`, - validate: { - params: paramSchema, - query: queryOptionsSchema, - }, - }, - router.handleLegacyErrors(async function ( - context: EventLogRequestHandlerContext, - req: KibanaRequest, FindOptionsType, unknown>, - res: KibanaResponseFactory - ): Promise { - if (!context.eventLog) { - return res.badRequest({ body: 'RouteHandlerContext is not registered for eventLog' }); - } - const eventLogClient = (await context.eventLog).getEventLogClient(); - const { - params: { id, type }, - query, - } = req; - - try { - return res.ok({ - body: await eventLogClient.findEventsBySavedObjectIds(type, [id], query), - }); - } catch (err) { - const call = `findEventsBySavedObjectIds(${type}, [${id}], ${JSON.stringify(query)})`; - systemLogger.debug(`error calling eventLog ${call}: ${err.message}`); - return res.notFound(); - } - }) - ); -}; diff --git a/x-pack/plugins/event_log/server/routes/find_by_ids.test.ts b/x-pack/plugins/event_log/server/routes/find_by_ids.test.ts deleted file mode 100644 index 81172f2119410..0000000000000 --- a/x-pack/plugins/event_log/server/routes/find_by_ids.test.ts +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { httpServiceMock } from '@kbn/core/server/mocks'; -import { mockHandlerArguments, fakeEvent } from './_mock_handler_arguments'; -import { eventLogClientMock } from '../event_log_client.mock'; -import { loggingSystemMock } from '@kbn/core/server/mocks'; -import { findByIdsRoute } from './find_by_ids'; - -const eventLogClient = eventLogClientMock.create(); -const systemLogger = loggingSystemMock.createLogger(); - -beforeEach(() => { - jest.resetAllMocks(); -}); - -describe('find_by_ids', () => { - it('finds events with proper parameters', async () => { - const router = httpServiceMock.createRouter(); - - findByIdsRoute(router, systemLogger); - - const [config, handler] = router.post.mock.calls[0]; - - expect(config.path).toMatchInlineSnapshot(`"/internal/event_log/{type}/_find"`); - - const events = [fakeEvent(), fakeEvent()]; - const result = { - page: 0, - per_page: 10, - total: events.length, - data: events, - }; - eventLogClient.findEventsBySavedObjectIds.mockResolvedValueOnce(result); - - const [context, req, res] = mockHandlerArguments( - eventLogClient, - { - params: { type: 'action' }, - body: { ids: ['1'], legacyIds: ['2'] }, - }, - ['ok'] - ); - - await handler(context, req, res); - - expect(eventLogClient.findEventsBySavedObjectIds).toHaveBeenCalledTimes(1); - - const [type, ids, , legacyIds] = eventLogClient.findEventsBySavedObjectIds.mock.calls[0]; - expect(type).toEqual(`action`); - expect(ids).toEqual(['1']); - expect(legacyIds).toEqual(['2']); - - expect(res.ok).toHaveBeenCalledWith({ - body: result, - }); - }); - - it('supports optional pagination parameters', async () => { - const router = httpServiceMock.createRouter(); - - findByIdsRoute(router, systemLogger); - - const [, handler] = router.post.mock.calls[0]; - eventLogClient.findEventsBySavedObjectIds.mockResolvedValueOnce({ - page: 0, - per_page: 10, - total: 0, - data: [], - }); - - const [context, req, res] = mockHandlerArguments( - eventLogClient, - { - params: { type: 'action' }, - body: { ids: ['1'] }, - query: { page: 3, per_page: 10 }, - }, - ['ok'] - ); - - await handler(context, req, res); - - expect(eventLogClient.findEventsBySavedObjectIds).toHaveBeenCalledTimes(1); - - const [type, id, options] = eventLogClient.findEventsBySavedObjectIds.mock.calls[0]; - expect(type).toEqual(`action`); - expect(id).toEqual(['1']); - expect(options).toMatchObject({}); - - expect(res.ok).toHaveBeenCalledWith({ - body: { - page: 0, - per_page: 10, - total: 0, - data: [], - }, - }); - }); - - it('logs a warning when the query throws an error', async () => { - const router = httpServiceMock.createRouter(); - - findByIdsRoute(router, systemLogger); - - const [, handler] = router.post.mock.calls[0]; - eventLogClient.findEventsBySavedObjectIds.mockRejectedValueOnce(new Error('oof!')); - - const [context, req, res] = mockHandlerArguments( - eventLogClient, - { - params: { type: 'action' }, - body: { ids: ['1'] }, - query: { page: 3, per_page: 10 }, - }, - ['ok'] - ); - - await handler(context, req, res); - - expect(systemLogger.debug).toHaveBeenCalledTimes(1); - expect(systemLogger.debug).toHaveBeenCalledWith( - 'error calling eventLog findEventsBySavedObjectIds(action, [1], {"page":3,"per_page":10}): oof!' - ); - }); -}); diff --git a/x-pack/plugins/event_log/server/routes/find_by_ids.ts b/x-pack/plugins/event_log/server/routes/find_by_ids.ts deleted file mode 100644 index a33a03c8242c4..0000000000000 --- a/x-pack/plugins/event_log/server/routes/find_by_ids.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { schema, TypeOf } from '@kbn/config-schema'; -import type { - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, - Logger, -} from '@kbn/core/server'; -import type { EventLogRouter, EventLogRequestHandlerContext } from '../types'; - -import { BASE_EVENT_LOG_API_PATH } from '../../common'; -import { queryOptionsSchema, FindOptionsType } from '../event_log_client'; - -const paramSchema = schema.object({ - type: schema.string(), -}); - -const bodySchema = schema.object({ - ids: schema.arrayOf(schema.string(), { defaultValue: [] }), - legacyIds: schema.arrayOf(schema.string(), { defaultValue: [] }), -}); - -export const findByIdsRoute = (router: EventLogRouter, systemLogger: Logger) => { - router.post( - { - path: `${BASE_EVENT_LOG_API_PATH}/{type}/_find`, - validate: { - params: paramSchema, - query: queryOptionsSchema, - body: bodySchema, - }, - }, - router.handleLegacyErrors(async function ( - context: EventLogRequestHandlerContext, - req: KibanaRequest, FindOptionsType, TypeOf>, - res: KibanaResponseFactory - ): Promise { - if (!context.eventLog) { - return res.badRequest({ body: 'RouteHandlerContext is not registered for eventLog' }); - } - const eventLogClient = (await context.eventLog).getEventLogClient(); - const { - params: { type }, - body: { ids, legacyIds }, - query, - } = req; - - try { - return res.ok({ - body: await eventLogClient.findEventsBySavedObjectIds(type, ids, query, legacyIds), - }); - } catch (err) { - const call = `findEventsBySavedObjectIds(${type}, [${ids}], ${JSON.stringify(query)})`; - systemLogger.debug(`error calling eventLog ${call}: ${err.message}`); - return res.notFound(); - } - }) - ); -}; diff --git a/x-pack/plugins/event_log/server/types.ts b/x-pack/plugins/event_log/server/types.ts index 6969ade7c3a8e..7287da0c0fd6e 100644 --- a/x-pack/plugins/event_log/server/types.ts +++ b/x-pack/plugins/event_log/server/types.ts @@ -6,7 +6,7 @@ */ import { schema, TypeOf } from '@kbn/config-schema'; -import type { IRouter, KibanaRequest, CustomRequestHandlerContext } from '@kbn/core/server'; +import type { KibanaRequest } from '@kbn/core/server'; import { KueryNode } from '@kbn/es-query'; export type { IEvent, IValidatedEvent } from '../generated/schemas'; @@ -84,22 +84,3 @@ export interface IEventLogger { startTiming(event: IEvent, startTime?: Date): void; stopTiming(event: IEvent): void; } - -/** - * @internal - */ -export interface EventLogApiRequestHandlerContext { - getEventLogClient(): IEventLogClient; -} - -/** - * @internal - */ -export type EventLogRequestHandlerContext = CustomRequestHandlerContext<{ - eventLog: EventLogApiRequestHandlerContext; -}>; - -/** - * @internal - */ -export type EventLogRouter = IRouter; diff --git a/x-pack/plugins/fleet/common/openapi/bundled.json b/x-pack/plugins/fleet/common/openapi/bundled.json index 6e7c9d8606955..34d2a8606daa2 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.json +++ b/x-pack/plugins/fleet/common/openapi/bundled.json @@ -581,9 +581,27 @@ "packages": { "type": "array", "items": { - "type": "string" + "oneOf": [ + { + "type": "string", + "description": "package name" + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "package name" + }, + "version": { + "type": "string", + "description": "package version" + } + } + } + ] }, - "description": "list of package names to install" + "description": "list of packages to install" }, "force": { "type": "boolean", @@ -3402,46 +3420,6 @@ }, "parameters": [] }, - "/message_signing_service/rotate_key_pair": { - "post": { - "summary": "Rotate key pair", - "tags": [ - "Message Signing Service" - ], - "operationId": "rotate-key-pair", - "parameters": [ - { - "schema": { - "type": "boolean" - }, - "in": "query", - "name": "acknowledge", - "required": true, - "description": "When set to true, rotate key pair is done. If set to false or missing, it returns an error." - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": { - "type": "string" - } - } - } - } - } - }, - "400": { - "$ref": "#/components/responses/error" - } - } - } - }, "/data_streams": { "get": { "summary": "List data streams", diff --git a/x-pack/plugins/fleet/common/openapi/bundled.yaml b/x-pack/plugins/fleet/common/openapi/bundled.yaml index a62444586efeb..dd697cadfa4ed 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.yaml +++ b/x-pack/plugins/fleet/common/openapi/bundled.yaml @@ -378,8 +378,18 @@ paths: packages: type: array items: - type: string - description: list of package names to install + oneOf: + - type: string + description: package name + - type: object + properties: + name: + type: string + description: package name + version: + type: string + description: package version + description: list of packages to install force: type: boolean description: force install to ignore package verification errors @@ -2113,33 +2123,6 @@ paths: parameters: - $ref: '#/components/parameters/kbn_xsrf' parameters: [] - /message_signing_service/rotate_key_pair: - post: - summary: Rotate key pair - tags: - - Message Signing Service - operationId: rotate-key-pair - parameters: - - schema: - type: boolean - in: query - name: acknowledge - required: true - description: >- - When set to true, rotate key pair is done. If set to false or - missing, it returns an error. - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - message: - type: string - '400': - $ref: '#/components/responses/error' /data_streams: get: summary: List data streams diff --git a/x-pack/plugins/fleet/common/openapi/entrypoint.yaml b/x-pack/plugins/fleet/common/openapi/entrypoint.yaml index ee95d9899a570..ca9d1cd3c8e19 100644 --- a/x-pack/plugins/fleet/common/openapi/entrypoint.yaml +++ b/x-pack/plugins/fleet/common/openapi/entrypoint.yaml @@ -105,10 +105,6 @@ paths: /agent_policies/delete: $ref: paths/agent_policies@delete.yaml - # Message signing service - /message_signing_service/rotate_key_pair: - $ref: paths/message_signing_service@rotate_key_pair.yaml - # Data streams endpoints /data_streams: $ref: paths/data_streams.yaml diff --git a/x-pack/plugins/fleet/common/openapi/paths/epm@packages_bulk.yaml b/x-pack/plugins/fleet/common/openapi/paths/epm@packages_bulk.yaml index a3775e69a7131..7a3b3f293cf87 100644 --- a/x-pack/plugins/fleet/common/openapi/paths/epm@packages_bulk.yaml +++ b/x-pack/plugins/fleet/common/openapi/paths/epm@packages_bulk.yaml @@ -29,8 +29,18 @@ post: packages: type: array items: - type: string - description: list of package names to install + oneOf: + - type: string + description: package name + - type: object + properties: + name: + type: string + description: package name + version: + type: string + description: package version + description: list of packages to install force: type: boolean description: force install to ignore package verification errors diff --git a/x-pack/plugins/fleet/common/openapi/paths/message_signing_service@rotate_key_pair.yaml b/x-pack/plugins/fleet/common/openapi/paths/message_signing_service@rotate_key_pair.yaml deleted file mode 100644 index 477967d3cb2b1..0000000000000 --- a/x-pack/plugins/fleet/common/openapi/paths/message_signing_service@rotate_key_pair.yaml +++ /dev/null @@ -1,24 +0,0 @@ -post: - summary: Rotate key pair - tags: - - Message Signing Service - operationId: rotate-key-pair - parameters: - - schema: - type: boolean - in: query - name: acknowledge - required: true - description: When set to true, rotate key pair is done. If set to false or missing, it returns an error. - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - message: - type: string - '400': - $ref: ../components/responses/error.yaml \ No newline at end of file diff --git a/x-pack/plugins/fleet/public/applications/fleet/app.tsx b/x-pack/plugins/fleet/public/applications/fleet/app.tsx index d947115861af9..99cf83aee7816 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/app.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/app.tsx @@ -30,7 +30,7 @@ import type { FleetConfigType, FleetStartServices } from '../../plugin'; import { PackageInstallProvider } from '../integrations/hooks'; -import { useAuthz, useFleetStatus, useFlyoutContext } from './hooks'; +import { type FleetStatusProviderProps, useAuthz, useFleetStatus, useFlyoutContext } from './hooks'; import { ConfigContext, @@ -240,6 +240,7 @@ export const FleetAppContext: React.FC<{ theme$: AppMountParameters['theme$']; /** For testing purposes only */ routerHistory?: History; + fleetStatus?: FleetStatusProviderProps; }> = memo( ({ children, @@ -250,6 +251,7 @@ export const FleetAppContext: React.FC<{ extensions, routerHistory, theme$, + fleetStatus, }) => { const isDarkMode = useObservable(startServices.uiSettings.get$('theme:darkMode')); @@ -265,7 +267,7 @@ export const FleetAppContext: React.FC<{ - + { return { ...jest.requireActual('../../../../hooks'), - FleetStatusProvider: (props: any) => { - return props.children; - }, - useFleetStatus: jest.fn().mockReturnValue({ isReady: true } as any), useGetPipeline: jest.fn(), }; }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/experimental_datastream_settings.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/experimental_datastream_settings.test.tsx index 2981e3cbca901..0b5cdc0f6ad91 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/experimental_datastream_settings.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/experimental_datastream_settings.test.tsx @@ -14,19 +14,6 @@ import type { RegistryDataStream } from '../../../../../../../../../common/types import { ExperimentDatastreamSettings } from './experimental_datastream_settings'; -jest.mock('../../../../../../../../hooks', () => { - return { - ...jest.requireActual('../../../../../../../../hooks'), - FleetStatusProvider: (props: any) => { - return props.children; - }, - useFleetStatus: jest.fn().mockReturnValue({ isReady: true } as any), - sendGetStatus: jest - .fn() - .mockResolvedValue({ data: { isReady: true, missing_requirements: [] } }), - }; -}); - describe('ExperimentDatastreamSettings', () => { describe('Synthetic source', () => { it('should be enabled an not checked by default', () => { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx index 9e77b746214e1..cc01cadccb788 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx @@ -257,9 +257,9 @@ export function useOnSubmit({ try { setFormState('LOADING'); if ((withSysMonitoring || newAgentPolicy.monitoring_enabled?.length) ?? 0 > 0) { - const packagesToPreinstall: string[] = []; + const packagesToPreinstall: Array = []; if (packageInfo) { - packagesToPreinstall.push(packageInfo.name); + packagesToPreinstall.push({ name: packageInfo.name, version: packageInfo.version }); } if (withSysMonitoring) { packagesToPreinstall.push(FLEET_SYSTEM_PACKAGE); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/action_menu.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/action_menu.test.tsx index c88b2c0aedcad..094e3b820ab9b 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/action_menu.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/action_menu.test.tsx @@ -15,12 +15,6 @@ import { useAuthz } from '../../../../../../hooks/use_authz'; import { AgentDetailsActionMenu } from './actions_menu'; -jest.mock('../../../../../../hooks/use_fleet_status', () => ({ - FleetStatusProvider: (props: any) => { - return props.children; - }, -})); - jest.mock('../../../../../../services/experimental_features'); jest.mock('../../../../../../hooks/use_authz'); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.test.tsx index 9af04b04761a6..5e1e8f773dedb 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.test.tsx @@ -7,29 +7,12 @@ import React from 'react'; -import { ThemeProvider } from 'styled-components'; - -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; - -import { coreMock } from '@kbn/core/public/mocks'; -import { registerTestBed } from '@kbn/test-jest-helpers'; - import type { Agent } from '../../../../types'; - -import { FleetStatusProvider, ConfigContext, KibanaVersionContext } from '../../../../../../hooks'; - -import { getMockTheme } from '../../../../../../mocks'; - +import { createFleetTestRendererMock } from '../../../../../../mock'; import { ExperimentalFeaturesService } from '../../../../services'; import { SearchAndFilterBar } from './search_and_filter_bar'; -const mockTheme = getMockTheme({ - eui: { - euiSize: '10px', - }, -}); - jest.mock('../../../../components', () => { return { SearchBar: () =>
, @@ -41,31 +24,24 @@ jest.mock('../../../../../../hooks/use_locator', () => { useDashboardLocator: jest.fn().mockImplementation(() => { return { id: 'DASHBOARD_APP_LOCATOR', - getRedirectUrl: jest.fn().mockResolvedValue('app/dashboards#/view/elastic_agent-a0002'), + getRedirectUrl: jest.fn().mockReturnValue('app/dashboards#/view/elastic_agent-a0002'), }; }), }; }); -const TestComponent = (props: any) => ( - - - - - - - - - - - -); - describe('SearchAndFilterBar', () => { beforeAll(() => { // @ts-ignore - prevents us needing to mock the entire service ExperimentalFeaturesService.init({}); }); + + function render(props: any) { + const renderer = createFleetTestRendererMock(); + + return renderer.render(); + } + it('should show no Actions button when no agent is selected', async () => { const selectedAgents: Agent[] = []; const props: any = { @@ -83,10 +59,8 @@ describe('SearchAndFilterBar', () => { selectedAgentPolicies: [], showAgentActivityTour: {}, }; - const testBed = registerTestBed(TestComponent)(props); - const { exists } = testBed; - - expect(exists('agentBulkActionsButton')).toBe(false); + const results = render(props); + expect(results.queryByTestId('agentBulkActionsButton')).toBeNull(); }); it('should show an Actions button when at least an agent is selected', async () => { @@ -117,10 +91,8 @@ describe('SearchAndFilterBar', () => { selectedAgentPolicies: [], showAgentActivityTour: {}, }; - const testBed = registerTestBed(TestComponent)(props); - const { exists } = testBed; - - expect(exists('agentBulkActionsButton')).not.toBeNull(); + const results = render(props); + expect(results.queryByTestId('agentBulkActionsButton')).not.toBeNull(); }); it('should show an Actions button when agents selected in query mode', async () => { @@ -139,9 +111,7 @@ describe('SearchAndFilterBar', () => { selectedAgentPolicies: [], showAgentActivityTour: {}, }; - const testBed = registerTestBed(TestComponent)(props); - const { exists } = testBed; - - expect(exists('agentBulkActionsButton')).not.toBeNull(); + const results = render(props); + expect(results.queryByTestId('agentBulkActionsButton')).not.toBeNull(); }); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.tsx index c9c527ba36e0a..e66a579fdab7a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.tsx @@ -37,7 +37,7 @@ const ClearAllTagsFilterItem = styled(EuiFilterSelectItem)` padding: ${(props) => props.theme.eui.euiSizeS}; `; -export const SearchAndFilterBar: React.FunctionComponent<{ +export interface SearchAndFilterBarProps { agentPolicies: AgentPolicy[]; draftKuery: string; onDraftKueryChange: (kuery: string) => void; @@ -62,7 +62,9 @@ export const SearchAndFilterBar: React.FunctionComponent<{ visibleAgents: Agent[]; onClickAgentActivity: () => void; showAgentActivityTour: { isOpen: boolean }; -}> = ({ +} + +export const SearchAndFilterBar: React.FunctionComponent = ({ agentPolicies, draftKuery, onDraftKueryChange, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/table_row_actions.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/table_row_actions.test.tsx index 810fe0f9617db..529bd41e4897e 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/table_row_actions.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/table_row_actions.test.tsx @@ -15,12 +15,6 @@ import { useAuthz } from '../../../../../../hooks/use_authz'; import { TableRowActions } from './table_row_actions'; -jest.mock('../../../../../../hooks/use_fleet_status', () => ({ - FleetStatusProvider: (props: any) => { - return props.children; - }, -})); - jest.mock('../../../../../../services/experimental_features'); jest.mock('../../../../../../hooks/use_authz'); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/hooks.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/hooks.test.tsx index 3f4fcea3ca5f7..8aea16559058a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/hooks.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/hooks.test.tsx @@ -12,13 +12,6 @@ import { createFleetTestRendererMock } from '../../../../../../mock'; import { useScheduleDateTime } from './hooks'; -jest.mock('../../../../../../hooks/use_fleet_status', () => ({ - FleetStatusProvider: (props: any) => { - return props.children; - }, - useFleetStatus: jest.fn().mockReturnValue({}), -})); - describe('useScheduleDateTime', () => { it('do not allow to set a date before the current time', async () => { const renderer = createFleetTestRendererMock(); 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 393b6519fc6c1..78619b976c49f 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 @@ -14,13 +14,6 @@ import { createFleetTestRendererMock } from '../../../../../../mock'; import { AgentUpgradeAgentModal } from '.'; import type { AgentUpgradeAgentModalProps } from '.'; -jest.mock('../../../../../../hooks/use_fleet_status', () => ({ - FleetStatusProvider: (props: any) => { - return props.children; - }, - useFleetStatus: jest.fn().mockReturnValue({}), -})); - jest.mock('@elastic/eui', () => { return { ...jest.requireActual('@elastic/eui'), diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.test.tsx index 20db1fc804d11..30ac08e0981a7 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/index.test.tsx @@ -14,9 +14,7 @@ import { useAuthz } from '../../../../hooks/use_authz'; import { AgentsApp } from '.'; jest.mock('../../../../hooks/use_fleet_status', () => ({ - FleetStatusProvider: (props: any) => { - return props.children; - }, + ...jest.requireActual('../../../../hooks/use_fleet_status'), useFleetStatus: jest.fn().mockReturnValue({}), })); jest.mock('../../../../hooks/use_request/settings'); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/download_source_flyout/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/download_source_flyout/index.test.tsx index 464d0601ced2b..1c7b1a28d560f 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/download_source_flyout/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/download_source_flyout/index.test.tsx @@ -12,13 +12,6 @@ import { createFleetTestRendererMock } from '../../../../../../mock'; import { EditDownloadSourceFlyout } from '.'; -jest.mock('../../../../../../hooks/use_fleet_status', () => ({ - FleetStatusProvider: (props: any) => { - return props.children; - }, - useFleetStatus: jest.fn().mockReturnValue({}), -})); - function renderFlyout(downloadSource?: DownloadSource) { const renderer = createFleetTestRendererMock(); diff --git a/x-pack/plugins/fleet/public/applications/integrations/app.tsx b/x-pack/plugins/fleet/public/applications/integrations/app.tsx index 68b40589d0b8e..bac899a06a9e6 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/app.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/app.tsx @@ -26,6 +26,7 @@ import type { FleetConfigType, FleetStartServices } from '../../plugin'; import { ConfigContext, FleetStatusProvider, + type FleetStatusProviderProps, KibanaVersionContext, useFleetStatus, } from '../../hooks'; @@ -61,6 +62,7 @@ export const IntegrationsAppContext: React.FC<{ theme$: AppMountParameters['theme$']; /** For testing purposes only */ routerHistory?: History; // TODO remove + fleetStatus?: FleetStatusProviderProps; }> = memo( ({ children, @@ -71,6 +73,7 @@ export const IntegrationsAppContext: React.FC<{ extensions, setHeaderActionMenu, theme$, + fleetStatus, }) => { const isDarkMode = useObservable(startServices.uiSettings.get$('theme:darkMode')); const CloudContext = startServices.cloud?.CloudContextProvider || EmptyContext; @@ -87,7 +90,7 @@ export const IntegrationsAppContext: React.FC<{ - + diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx index a64425d28ae04..63a621f166520 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.test.tsx @@ -46,6 +46,7 @@ describe('when on integration detail', () => { let testRenderer: TestRenderer; let renderResult: ReturnType; let mockedApi: MockedApi; + const render = async () => { await act(async () => { renderResult = testRenderer.render( @@ -67,16 +68,15 @@ describe('when on integration detail', () => { }); describe('and the package is installed', () => { - beforeEach(async () => render()); + beforeEach(async () => await render()); it('should display agent policy usage count', async () => { await act(() => mockedApi.waitForApi()); - expect(renderResult.queryByTestId('agentPolicyCount')).not.toBeNull(); }); it('should show the Policies tab', async () => { - await mockedApi.waitForApi(); + await act(() => mockedApi.waitForApi()); expect(renderResult.queryByTestId('tab-policies')).not.toBeNull(); }); }); @@ -85,6 +85,7 @@ describe('when on integration detail', () => { const unInstalledPackage = mockedApi.responseProvider.epmGetInfo('nginx'); unInstalledPackage.item.status = 'not_installed'; unInstalledPackage.item.version = pkgVersion; + mockedApi.responseProvider.epmGetInfo.mockImplementation((name, version, query) => { if (query?.prerelease === false) { const gaPackage = { item: { ...unInstalledPackage.item } }; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.tsx similarity index 83% rename from x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts rename to x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.tsx index 661c72fbc5118..8f9b13671ce65 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import React from 'react'; // TODO(jbudz): should be removed when upgrading to TS@4.8 // this is a skip for the errors created when typechecking with isolatedModules export {}; @@ -12,7 +13,6 @@ export {}; jest.mock('../../hooks', () => { return { ...jest.requireActual('../../hooks'), - useFleetStatus: jest.fn(), useFleetServerStandalone: jest.fn(), useAgentEnrollmentFlyoutData: jest.fn(), }; @@ -96,14 +96,28 @@ jest.mock('./steps', () => { AgentPolicySelectionStep: jest.fn().mockReturnValue({ 'data-test-subj': 'agent-policy-selection-step', title: 'agent-policy-selection-step', + children: <>TEST, }), AgentEnrollmentKeySelectionStep: jest.fn().mockReturnValue({ 'data-test-subj': 'agent-enrollment-key-selection-step', title: 'agent-enrollment-key-selection-step', + children: <>TEST, + }), + ConfigureStandaloneAgentStep: jest.fn().mockReturnValue({ + 'data-test-subj': 'configure-standalone-step', + title: 'configure-standalone-step', + children: <>TEST, + }), + DownloadStep: jest.fn().mockReturnValue({ + 'data-test-subj': 'download-step', + title: 'download-step', + children: <>TEST, + }), + IncomingDataConfirmationStep: jest.fn().mockReturnValue({ + 'data-test-subj': 'incoming-data-confirmation-step', + title: 'incoming-data-confirmation-step', + children: <>TEST, }), - DownloadStep: jest - .fn() - .mockReturnValue({ 'data-test-subj': 'download-step', title: 'download-step' }), }; }); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx index b2a070ac93c55..5f6d46e5e2573 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx @@ -8,27 +8,17 @@ import './agent_enrollment_flyout.test.mocks'; import React from 'react'; -import { registerTestBed } from '@kbn/test-jest-helpers'; -import { act } from '@testing-library/react'; - -import { coreMock } from '@kbn/core/public/mocks'; - -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { act, cleanup, fireEvent, waitFor } from '@testing-library/react'; +import type { RenderResult } from '@testing-library/react'; +import { createFleetTestRendererMock } from '../../mock'; import type { AgentPolicy } from '../../../common'; import { useGetFleetServerHosts, sendGetOneAgentPolicy, useGetAgents, } from '../../hooks/use_request'; -import { - FleetStatusProvider, - ConfigContext, - useAgentEnrollmentFlyoutData, - KibanaVersionContext, - useFleetStatus, - useFleetServerStandalone, -} from '../../hooks'; +import { useAgentEnrollmentFlyoutData, useFleetServerStandalone } from '../../hooks'; import { useAdvancedForm } from '../../applications/fleet/components/fleet_server_instructions/hooks'; import { useFleetServerUnhealthy } from '../../applications/fleet/sections/agents/hooks/use_fleet_server_unhealthy'; @@ -36,36 +26,13 @@ import { useFleetServerUnhealthy } from '../../applications/fleet/sections/agent import type { FlyOutProps } from './types'; import { AgentEnrollmentFlyout } from '.'; -const TestComponent = (props: FlyOutProps) => ( - - - - - - - - - -); - -const setup = (props?: FlyOutProps) => { - const testBed = registerTestBed(TestComponent)(props); - const { find, component } = testBed; - - return { - ...testBed, - actions: { - goToStandaloneTab: () => - act(() => { - find('agentEnrollmentFlyout.standaloneTab').simulate('click'); - component.update(); - }), - }, - }; -}; +const render = (props?: Partial) => { + cleanup(); + const renderer = createFleetTestRendererMock(); + const results = renderer.render(); -type SetupReturn = ReturnType; -type TestBed = SetupReturn extends Promise ? U : SetupReturn; + return results; +}; const testAgentPolicy: AgentPolicy = { id: 'test', @@ -80,9 +47,9 @@ const testAgentPolicy: AgentPolicy = { }; describe('', () => { - let testBed: TestBed; + let results: RenderResult; - beforeEach(() => { + beforeEach(async () => { (useGetFleetServerHosts as jest.Mock).mockReturnValue({ data: { items: [ @@ -95,7 +62,6 @@ describe('', () => { }); jest.mocked(useFleetServerStandalone).mockReturnValue({ isFleetServerStandalone: false }); - (useFleetStatus as jest.Mock).mockReturnValue({ isReady: true }); (useFleetServerUnhealthy as jest.Mock).mockReturnValue({ isLoading: false, isUnhealthy: false, @@ -135,10 +101,7 @@ describe('', () => { }); act(() => { - testBed = setup({ - onClose: jest.fn(), - }); - testBed.component.update(); + results = render(); }); }); @@ -153,98 +116,93 @@ describe('', () => { isLoadingInitialAgentPolicies: true, }); - act(() => { - testBed = setup({ - onClose: jest.fn(), - }); - testBed.component.update(); - }); + results = render(); - const { exists } = testBed; - expect(exists('agentEnrollmentFlyout')).toBe(true); - expect(exists('loadingSpinner')).toBe(true); + expect(results.queryByTestId('agentEnrollmentFlyout')).not.toBeNull(); + expect(results.queryByTestId('loadingSpinner')).not.toBeNull(); }); describe('managed instructions', () => { it('uses the agent policy selection step', () => { - const { exists } = testBed; - expect(exists('agentEnrollmentFlyout')).toBe(true); - expect(exists('agent-policy-selection-step')).toBe(true); - expect(exists('agent-enrollment-key-selection-step')).toBe(false); + expect(results.queryByTestId('agentEnrollmentFlyout')).not.toBeNull(); + expect(results.queryByTestId('agent-policy-selection-step')).not.toBeNull(); + expect(results.queryByTestId('agent-enrollment-key-selection-step')).toBeNull(); }); describe('with a specific policy', () => { - beforeEach(() => { - jest.clearAllMocks(); - act(() => { - testBed = setup({ - agentPolicy: testAgentPolicy, - onClose: jest.fn(), - }); - testBed.component.update(); + beforeEach(async () => { + results = render({ + agentPolicy: testAgentPolicy, + }); + await waitFor(() => { + expect(sendGetOneAgentPolicy).toBeCalled(); }); }); it('uses the configure enrollment step, not the agent policy selection step', () => { - const { exists } = testBed; - expect(exists('agentEnrollmentFlyout')).toBe(true); - expect(exists('agent-policy-selection-step')).toBe(false); - expect(exists('agent-enrollment-key-selection-step')).toBe(true); + expect(results.queryByTestId('agentEnrollmentFlyout')).not.toBeNull(); + expect(results.queryByTestId('agent-policy-selection-step')).toBeNull(); + expect(results.queryByTestId('agent-enrollment-key-selection-step')).not.toBeNull(); }); }); describe('with a specific policy when no agentPolicies set', () => { - beforeEach(() => { + beforeEach(async () => { jest.clearAllMocks(); - act(() => { - testBed = setup({ - agentPolicy: testAgentPolicy, - onClose: jest.fn(), - }); - testBed.component.update(); + results = render({ + agentPolicy: testAgentPolicy, + }); + await waitFor(() => { + expect(sendGetOneAgentPolicy).toBeCalled(); }); }); it('should not show fleet server instructions', () => { - const { exists } = testBed; - expect(exists('agentEnrollmentFlyout')).toBe(true); - expect(exists('agent-enrollment-key-selection-step')).toBe(true); + expect(results.queryByTestId('agentEnrollmentFlyout')).not.toBeNull(); + expect(results.queryByTestId('agent-enrollment-key-selection-step')).not.toBeNull(); }); }); - // Skipped due to UI changing in https://github.com/elastic/kibana/issues/125534. These tests should be rethought overall - // to provide value around the new flyout structure - describe.skip('standalone instructions', () => { + describe('standalone instructions', () => { + function goToStandaloneTab() { + act(() => { + fireEvent.click(results.getByTestId('standaloneTab')); + }); + } + + beforeEach(() => { + results = render({ + isIntegrationFlow: true, + }); + }); + it('uses the agent policy selection step', async () => { - const { exists, actions } = testBed; - actions.goToStandaloneTab(); + goToStandaloneTab(); - expect(exists('agentEnrollmentFlyout')).toBe(true); - expect(exists('agent-policy-selection-step')).toBe(true); - expect(exists('agent-enrollment-key-selection-step')).toBe(false); + expect(results.queryByTestId('agentEnrollmentFlyout')).not.toBeNull(); + expect(results.queryByTestId('agent-policy-selection-step')).not.toBeNull(); + expect(results.queryByTestId('agent-enrollment-key-selection-step')).toBeNull(); + expect(results.queryByTestId('configure-standalone-step')).not.toBeNull(); }); describe('with a specific policy', () => { - beforeEach(() => { - jest.clearAllMocks(); - act(() => { - testBed = setup({ - agentPolicy: testAgentPolicy, - onClose: jest.fn(), - }); - testBed.component.update(); + beforeEach(async () => { + results = render({ + isIntegrationFlow: true, + agentPolicy: testAgentPolicy, + }); + await waitFor(() => { + expect(sendGetOneAgentPolicy).toBeCalled(); }); }); it('does not use either of the agent policy selection or enrollment key steps', () => { - const { exists, actions } = testBed; - jest.clearAllMocks(); - - actions.goToStandaloneTab(); + goToStandaloneTab(); - expect(exists('agentEnrollmentFlyout')).toBe(true); - expect(exists('agent-policy-selection-step')).toBe(false); - expect(exists('agent-enrollment-key-selection-step')).toBe(false); + expect(results.queryByTestId('agentEnrollmentFlyout')).not.toBeNull(); + expect(results.queryByTestId('agent-policy-selection-step')).toBeNull(); + expect(results.queryByTestId('agent-enrollment-key-selection-step')).toBeNull(); + expect(results.queryByTestId('configure-standalone-step')).not.toBeNull(); }); }); }); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx index ad2a5339aeea1..3a965d9f4e81f 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx @@ -62,9 +62,6 @@ export const Instructions = (props: InstructionProps) => { const fleetServers = agents?.items || []; - if (isLoadingAgents || isLoadingAgentPolicies || isLoadingFleetServerHealth) - return ; - const hasNoFleetServerHost = fleetStatus.isReady && (fleetServerHosts?.length ?? 0) === 0; const showAgentEnrollment = @@ -81,12 +78,16 @@ export const Instructions = (props: InstructionProps) => { (fleetServers.length === 0 || isFleetServerUnhealthy || (fleetStatus.missingRequirements ?? []).some((r) => r === FLEET_SERVER_PACKAGE)); + useEffect(() => { + if (!isIntegrationFlow && showAgentEnrollment) { + setSelectionType('radio'); + } else { + setSelectionType('tabs'); + } + }, [isIntegrationFlow, showAgentEnrollment, setSelectionType]); - if (!isIntegrationFlow && showAgentEnrollment) { - setSelectionType('radio'); - } else { - setSelectionType('tabs'); - } + if (isLoadingAgents || isLoadingAgentPolicies || isLoadingFleetServerHealth) + return ; if (hasNoFleetServerHost) { return null; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_managed_agent_step.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_managed_agent_step.tsx index 997efe59c8fe6..8274939da374f 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_managed_agent_step.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/install_managed_agent_step.tsx @@ -46,15 +46,18 @@ export const InstallManagedAgentStep = ({ title: i18n.translate('xpack.fleet.agentEnrollment.stepEnrollAndRunAgentTitle', { defaultMessage: 'Install Elastic Agent on your host', }), - children: selectedApiKeyId && apiKeyData && ( - - ), + children: + selectedApiKeyId && apiKeyData ? ( + + ) : ( + + ), }; }; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/installation_mode_selection_step.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/installation_mode_selection_step.tsx index c5213f061bceb..17dc6df6926ba 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/installation_mode_selection_step.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/installation_mode_selection_step.tsx @@ -86,6 +86,8 @@ export const InstallationModeSelectionStep = ({ onChange={onChangeCallback} name={`radio group ${radioSuffix}`} /> - ) : null, + ) : ( + + ), }; }; diff --git a/x-pack/plugins/fleet/public/components/link_and_revision.test.tsx b/x-pack/plugins/fleet/public/components/link_and_revision.test.tsx index f2d0a8970cfa3..46fdfe69c63bd 100644 --- a/x-pack/plugins/fleet/public/components/link_and_revision.test.tsx +++ b/x-pack/plugins/fleet/public/components/link_and_revision.test.tsx @@ -14,13 +14,6 @@ import type { AgentPolicy, Agent } from '../types'; import { AgentPolicySummaryLine } from './link_and_revision'; -jest.mock('../hooks/use_fleet_status', () => ({ - FleetStatusProvider: (props: any) => { - return props.children; - }, - useFleetStatus: jest.fn().mockReturnValue({}), -})); - describe('AgentPolicySummaryLine', () => { let testRenderer: TestRenderer; diff --git a/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx b/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx index d0f3414a4d016..62c1a8f814f2a 100644 --- a/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx +++ b/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx @@ -12,7 +12,7 @@ import type { GetFleetStatusResponse } from '../types'; import { useConfig } from './use_config'; import { sendGetFleetStatus } from './use_request'; -interface FleetStatusState { +export interface FleetStatusProviderProps { enabled: boolean; isLoading: boolean; isReady: boolean; @@ -21,7 +21,7 @@ interface FleetStatusState { missingOptionalFeatures?: GetFleetStatusResponse['missing_optional_features']; } -interface FleetStatus extends FleetStatusState { +interface FleetStatus extends FleetStatusProviderProps { refresh: () => Promise; // This flag allows us to opt into displaying the Fleet Server enrollment instructions even if @@ -33,15 +33,19 @@ interface FleetStatus extends FleetStatusState { const FleetStatusContext = React.createContext(undefined); -export const FleetStatusProvider: React.FC = ({ children }) => { +export const FleetStatusProvider: React.FC<{ + defaultFleetStatus?: FleetStatusProviderProps; +}> = ({ defaultFleetStatus, children }) => { const config = useConfig(); const [forceDisplayInstructions, setForceDisplayInstructions] = useState(false); - const [state, setState] = useState({ - enabled: config.agents.enabled, - isLoading: false, - isReady: false, - }); + const [state, setState] = useState( + defaultFleetStatus ?? { + enabled: config.agents.enabled, + isLoading: false, + isReady: false, + } + ); // TODO: Refactor to use react-query const sendGetStatus = useCallback( @@ -68,8 +72,10 @@ export const FleetStatusProvider: React.FC = ({ children }) => { ); useEffect(() => { - sendGetStatus(); - }, [sendGetStatus]); + if (!defaultFleetStatus) { + sendGetStatus(); + } + }, [sendGetStatus, defaultFleetStatus]); const refresh = useCallback(() => sendGetStatus(), [sendGetStatus]); diff --git a/x-pack/plugins/fleet/public/hooks/use_request/epm.ts b/x-pack/plugins/fleet/public/hooks/use_request/epm.ts index f050820508d98..c1cd11f6a8092 100644 --- a/x-pack/plugins/fleet/public/hooks/use_request/epm.ts +++ b/x-pack/plugins/fleet/public/hooks/use_request/epm.ts @@ -204,7 +204,9 @@ export const sendInstallPackage = (pkgName: string, pkgVersion: string, force: b }); }; -export const sendBulkInstallPackages = (packages: string[]) => { +export const sendBulkInstallPackages = ( + packages: Array +) => { return sendRequest({ path: epmRouteService.getBulkInstallPath(), method: 'post', diff --git a/x-pack/plugins/fleet/public/mock/create_test_renderer.tsx b/x-pack/plugins/fleet/public/mock/create_test_renderer.tsx index 778dcdb32bc47..f0861582fd65f 100644 --- a/x-pack/plugins/fleet/public/mock/create_test_renderer.tsx +++ b/x-pack/plugins/fleet/public/mock/create_test_renderer.tsx @@ -96,6 +96,11 @@ export const createFleetTestRendererMock = (): TestRenderer => { extensions={extensions} routerHistory={testRendererMocks.history} theme$={themeServiceMock.createTheme$()} + fleetStatus={{ + enabled: true, + isLoading: false, + isReady: true, + }} > {children} @@ -156,6 +161,11 @@ export const createIntegrationsTestRendererMock = (): TestRenderer => { routerHistory={testRendererMocks.history} theme$={themeServiceMock.createTheme$()} setHeaderActionMenu={() => {}} + fleetStatus={{ + enabled: true, + isLoading: false, + isReady: true, + }} > {children} diff --git a/x-pack/plugins/fleet/server/types/models/preconfiguration.ts b/x-pack/plugins/fleet/server/types/models/preconfiguration.ts index a8b121aa9c374..65427544420f5 100644 --- a/x-pack/plugins/fleet/server/types/models/preconfiguration.ts +++ b/x-pack/plugins/fleet/server/types/models/preconfiguration.ts @@ -134,7 +134,7 @@ export const PreconfiguredAgentPoliciesSchema = schema.arrayOf( if (value === 'synthetics') { return i18n.translate('xpack.fleet.config.disableSynthetics', { defaultMessage: - 'Synthetics package is not supported via kibana.yml config. Please use synthetics app to create monitors in private locations. https://www.elastic.co/guide/en/observability/current/synthetics-private-location.html', + 'Synthetics package is not supported via kibana.yml config. Please use Synthetics App to create monitors in private locations. https://www.elastic.co/guide/en/observability/current/synthetics-private-location.html', }); } }, diff --git a/x-pack/plugins/fleet/server/types/rest_spec/epm.ts b/x-pack/plugins/fleet/server/types/rest_spec/epm.ts index 56187c62025c6..204183a056b1b 100644 --- a/x-pack/plugins/fleet/server/types/rest_spec/epm.ts +++ b/x-pack/plugins/fleet/server/types/rest_spec/epm.ts @@ -134,7 +134,13 @@ export const BulkInstallPackagesFromRegistryRequestSchema = { prerelease: schema.maybe(schema.boolean()), }), body: schema.object({ - packages: schema.arrayOf(schema.string(), { minSize: 1 }), + packages: schema.arrayOf( + schema.oneOf([ + schema.string(), + schema.object({ name: schema.string(), version: schema.string() }), + ]), + { minSize: 1 } + ), force: schema.boolean({ defaultValue: false }), }), }; diff --git a/x-pack/plugins/fleet/server/types/rest_spec/message_signing_service.test.ts b/x-pack/plugins/fleet/server/types/rest_spec/message_signing_service.test.ts index fa1f34164ea99..cc06bd5acae5e 100644 --- a/x-pack/plugins/fleet/server/types/rest_spec/message_signing_service.test.ts +++ b/x-pack/plugins/fleet/server/types/rest_spec/message_signing_service.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { RotateKeyPairSchema } from './message_signing_service'; +import { errorMessage, RotateKeyPairSchema } from './message_signing_service'; describe('RotateKeyPairSchema', () => { it('should throw on `false` values for acknowledge', () => { @@ -13,15 +13,11 @@ describe('RotateKeyPairSchema', () => { RotateKeyPairSchema.query.validate({ acknowledge: false, }) - ).toThrowError( - 'You must acknowledge the risks of rotating the key pair with acknowledge=true in the request parameters.' - ); + ).toThrowError(errorMessage); }); it('should allow without any query', () => { - expect(() => RotateKeyPairSchema.query.validate({})).toThrowError( - 'You must acknowledge the risks of rotating the key pair with acknowledge=true in the request parameters.' - ); + expect(() => RotateKeyPairSchema.query.validate({})).toThrowError(errorMessage); }); it.each([1, 'string'])('should not allow non-boolean `%s` values for acknowledge', (value) => { diff --git a/x-pack/plugins/fleet/server/types/rest_spec/message_signing_service.ts b/x-pack/plugins/fleet/server/types/rest_spec/message_signing_service.ts index d6037748e4682..e643fd0505618 100644 --- a/x-pack/plugins/fleet/server/types/rest_spec/message_signing_service.ts +++ b/x-pack/plugins/fleet/server/types/rest_spec/message_signing_service.ts @@ -7,6 +7,8 @@ import { schema } from '@kbn/config-schema'; +export const errorMessage = + 'Warning: this API will cause a key pair to rotate and should not be necessary in normal operation. If you proceed, you may need to reinstall Agents in your network. You must acknowledge the risks of rotating the key pair with acknowledge=true in the request parameters. For more information, reach out to your administrator.'; export const RotateKeyPairSchema = { query: schema.maybe( schema.object( @@ -19,9 +21,7 @@ export const RotateKeyPairSchema = { defaultValue: { acknowledge: false }, validate: (value: { acknowledge: boolean }) => { if (!value || !value.acknowledge) { - throw new Error( - 'You must acknowledge the risks of rotating the key pair with acknowledge=true in the request parameters.' - ); + throw new Error(errorMessage); } }, } diff --git a/x-pack/plugins/fleet/tsconfig.json b/x-pack/plugins/fleet/tsconfig.json index f80d7d51686d8..0aafb8faacc28 100644 --- a/x-pack/plugins/fleet/tsconfig.json +++ b/x-pack/plugins/fleet/tsconfig.json @@ -63,7 +63,6 @@ "@kbn/global-search-plugin", "@kbn/utility-types", "@kbn/utility-types-jest", - "@kbn/test-jest-helpers", "@kbn/es-query", "@kbn/ui-theme", "@kbn/rison", diff --git a/x-pack/plugins/infra/common/alerting/metrics/types.ts b/x-pack/plugins/infra/common/alerting/metrics/types.ts index 38a6542b9f788..5a9105ed2e19d 100644 --- a/x-pack/plugins/infra/common/alerting/metrics/types.ts +++ b/x-pack/plugins/infra/common/alerting/metrics/types.ts @@ -6,8 +6,8 @@ */ import * as rt from 'io-ts'; import { TimeUnitChar } from '@kbn/observability-plugin/common/utils/formatters/duration'; +import { ML_ANOMALY_THRESHOLD } from '@kbn/ml-anomaly-utils/anomaly_threshold'; import { SnapshotCustomMetricInput } from '../../http_api'; -import { ANOMALY_THRESHOLD } from '../../infra_ml'; import { InventoryItemType, SnapshotMetricType } from '../../inventory_models/types'; export const METRIC_THRESHOLD_ALERT_TYPE_ID = 'metrics.alert.threshold'; @@ -61,7 +61,7 @@ export interface MetricAnomalyParams { alertInterval?: string; sourceId?: string; spaceId?: string; - threshold: Exclude; + threshold: Exclude; influencerFilter: rt.TypeOf | undefined; } diff --git a/x-pack/plugins/infra/common/infra_ml/anomaly_results.ts b/x-pack/plugins/infra/common/infra_ml/anomaly_results.ts index 81e46d85ba220..524552b306881 100644 --- a/x-pack/plugins/infra/common/infra_ml/anomaly_results.ts +++ b/x-pack/plugins/infra/common/infra_ml/anomaly_results.ts @@ -5,44 +5,21 @@ * 2.0. */ -export enum ANOMALY_SEVERITY { - CRITICAL = 'critical', - MAJOR = 'major', - MINOR = 'minor', - WARNING = 'warning', - LOW = 'low', - UNKNOWN = 'unknown', -} - -export enum ANOMALY_THRESHOLD { - CRITICAL = 75, - MAJOR = 50, - MINOR = 25, - WARNING = 3, - LOW = 0, -} - -export const SEVERITY_COLORS = { - CRITICAL: '#fe5050', - MAJOR: '#fba740', - MINOR: '#fdec25', - WARNING: '#8bc8fb', - LOW: '#d2e9f7', - BLANK: '#ffffff', -}; - -export const getSeverityCategoryForScore = (score: number): ANOMALY_SEVERITY | undefined => { - if (score >= ANOMALY_THRESHOLD.CRITICAL) { - return ANOMALY_SEVERITY.CRITICAL; - } else if (score >= ANOMALY_THRESHOLD.MAJOR) { - return ANOMALY_SEVERITY.MAJOR; - } else if (score >= ANOMALY_THRESHOLD.MINOR) { - return ANOMALY_SEVERITY.MINOR; - } else if (score >= ANOMALY_THRESHOLD.WARNING) { - return ANOMALY_SEVERITY.WARNING; +import { ML_ANOMALY_SEVERITY } from '@kbn/ml-anomaly-utils/anomaly_severity'; +import { ML_ANOMALY_THRESHOLD } from '@kbn/ml-anomaly-utils/anomaly_threshold'; + +export const getSeverityCategoryForScore = (score: number): ML_ANOMALY_SEVERITY | undefined => { + if (score >= ML_ANOMALY_THRESHOLD.CRITICAL) { + return ML_ANOMALY_SEVERITY.CRITICAL; + } else if (score >= ML_ANOMALY_THRESHOLD.MAJOR) { + return ML_ANOMALY_SEVERITY.MAJOR; + } else if (score >= ML_ANOMALY_THRESHOLD.MINOR) { + return ML_ANOMALY_SEVERITY.MINOR; + } else if (score >= ML_ANOMALY_THRESHOLD.WARNING) { + return ML_ANOMALY_SEVERITY.WARNING; } else { // Category is too low to include - return ANOMALY_SEVERITY.LOW; + return ML_ANOMALY_SEVERITY.LOW; } }; diff --git a/x-pack/plugins/infra/public/alerting/metric_anomaly/components/expression.tsx b/x-pack/plugins/infra/public/alerting/metric_anomaly/components/expression.tsx index 1e54c890d6245..37db6cb499f30 100644 --- a/x-pack/plugins/infra/public/alerting/metric_anomaly/components/expression.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_anomaly/components/expression.tsx @@ -15,9 +15,9 @@ import { RuleTypeParamsExpressionProps, WhenExpression, } from '@kbn/triggers-actions-ui-plugin/public'; +import { ML_ANOMALY_THRESHOLD } from '@kbn/ml-anomaly-utils/anomaly_threshold'; import { useSourceContext, withSourceProvider } from '../../../containers/metrics_source'; import { MetricAnomalyParams } from '../../../../common/alerting/metrics'; -import { ANOMALY_THRESHOLD } from '../../../../common/infra_ml'; import { findInventoryModel } from '../../../../common/inventory_models'; import { InventoryItemType, SnapshotMetricType } from '../../../../common/inventory_models/types'; import { SubscriptionSplashPrompt } from '../../../components/subscription_splash_content'; @@ -43,7 +43,7 @@ type Props = Omit< export const defaultExpression = { metric: 'memory_usage' as MetricAnomalyParams['metric'], - threshold: ANOMALY_THRESHOLD.MAJOR as MetricAnomalyParams['threshold'], + threshold: ML_ANOMALY_THRESHOLD.MAJOR as MetricAnomalyParams['threshold'], nodeType: 'hosts' as MetricAnomalyParams['nodeType'], influencerFilter: undefined, }; @@ -220,7 +220,7 @@ export const Expression: React.FC = (props) => { diff --git a/x-pack/plugins/infra/public/alerting/metric_anomaly/components/severity_threshold.tsx b/x-pack/plugins/infra/public/alerting/metric_anomaly/components/severity_threshold.tsx index d93e088fe25a7..f0de1d0d30e7e 100644 --- a/x-pack/plugins/infra/public/alerting/metric_anomaly/components/severity_threshold.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_anomaly/components/severity_threshold.tsx @@ -10,11 +10,11 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiExpression, EuiPopover, EuiFlexGroup, EuiFlexItem, EuiSelect } from '@elastic/eui'; import { EuiPopoverTitle, EuiButtonIcon } from '@elastic/eui'; -import { ANOMALY_THRESHOLD } from '../../../../common/infra_ml'; +import { ML_ANOMALY_THRESHOLD } from '@kbn/ml-anomaly-utils/anomaly_threshold'; interface WhenExpressionProps { - value: Exclude; - onChange: (value: ANOMALY_THRESHOLD) => void; + value: Exclude; + onChange: (value: ML_ANOMALY_THRESHOLD) => void; popupPosition?: | 'upCenter' | 'upLeft' @@ -31,29 +31,29 @@ interface WhenExpressionProps { } const options = { - [ANOMALY_THRESHOLD.CRITICAL]: { + [ML_ANOMALY_THRESHOLD.CRITICAL]: { text: i18n.translate('xpack.infra.metrics.alertFlyout.expression.severityScore.criticalLabel', { defaultMessage: 'Critical', }), - value: ANOMALY_THRESHOLD.CRITICAL, + value: ML_ANOMALY_THRESHOLD.CRITICAL, }, - [ANOMALY_THRESHOLD.MAJOR]: { + [ML_ANOMALY_THRESHOLD.MAJOR]: { text: i18n.translate('xpack.infra.metrics.alertFlyout.expression.severityScore.majorLabel', { defaultMessage: 'Major', }), - value: ANOMALY_THRESHOLD.MAJOR, + value: ML_ANOMALY_THRESHOLD.MAJOR, }, - [ANOMALY_THRESHOLD.MINOR]: { + [ML_ANOMALY_THRESHOLD.MINOR]: { text: i18n.translate('xpack.infra.metrics.alertFlyout.expression.severityScore.minorLabel', { defaultMessage: 'Minor', }), - value: ANOMALY_THRESHOLD.MINOR, + value: ML_ANOMALY_THRESHOLD.MINOR, }, - [ANOMALY_THRESHOLD.WARNING]: { + [ML_ANOMALY_THRESHOLD.WARNING]: { text: i18n.translate('xpack.infra.metrics.alertFlyout.expression.severityScore.warningLabel', { defaultMessage: 'Warning', }), - value: ANOMALY_THRESHOLD.WARNING, + value: ML_ANOMALY_THRESHOLD.WARNING, }, }; @@ -101,7 +101,7 @@ export const SeverityThresholdExpression = ({ value={value} fullWidth onChange={(e) => { - onChange(Number(e.target.value) as ANOMALY_THRESHOLD); + onChange(Number(e.target.value) as ML_ANOMALY_THRESHOLD); setAggTypePopoverOpen(false); }} options={Object.values(options).map((o) => o)} diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_results/anomaly_severity_indicator.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_results/anomaly_severity_indicator.tsx index 87ab4eaa4d627..f4427ba615235 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_results/anomaly_severity_indicator.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_results/anomaly_severity_indicator.tsx @@ -7,7 +7,7 @@ import { EuiHealth } from '@elastic/eui'; import React, { useMemo } from 'react'; -import { getFormattedSeverityScore } from '@kbn/ml-plugin/public'; +import { getFormattedSeverityScore } from '@kbn/ml-anomaly-utils/get_formatted_severity_score'; import { getSeverityCategoryForScore, ML_SEVERITY_COLORS } from '../../../../common/log_analysis'; export const AnomalySeverityIndicator: React.FunctionComponent<{ diff --git a/x-pack/plugins/infra/public/components/saved_views/manage_views_flyout.tsx b/x-pack/plugins/infra/public/components/saved_views/manage_views_flyout.tsx index 33e5b71c56a3f..e503bdebafa03 100644 --- a/x-pack/plugins/infra/public/components/saved_views/manage_views_flyout.tsx +++ b/x-pack/plugins/infra/public/components/saved_views/manage_views_flyout.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState } from 'react'; +import React, { useMemo } from 'react'; import useToggle from 'react-use/lib/useToggle'; import { @@ -22,16 +22,23 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { SavedView } from '../../containers/saved_view/saved_view'; +import { EuiBasicTableColumn } from '@elastic/eui'; +import { EuiButtonIcon } from '@elastic/eui'; +import { MetricsExplorerView } from '../../../common/metrics_explorer_views'; +import type { InventoryView } from '../../../common/inventory_views'; +import { UseInventoryViewsResult } from '../../hooks/use_inventory_views'; +import { UseMetricsExplorerViewsResult } from '../../hooks/use_metrics_explorer_views'; -interface Props { - views: Array>; +type View = InventoryView | MetricsExplorerView; +type UseViewResult = UseInventoryViewsResult | UseMetricsExplorerViewsResult; + +export interface ManageViewsFlyoutProps { + views: UseViewResult['views']; loading: boolean; - sourceIsLoading: boolean; onClose(): void; - onMakeDefaultView(id: string): void; - setView(viewState: ViewState): void; - onDeleteView(id: string): void; + onMakeDefaultView: UseViewResult['setDefaultViewById']; + onSwitchView: UseViewResult['switchViewById']; + onDeleteView: UseViewResult['deleteViewById']; } interface DeleteConfimationProps { @@ -39,55 +46,27 @@ interface DeleteConfimationProps { onConfirm(): void; } -const DeleteConfimation = ({ isDisabled, onConfirm }: DeleteConfimationProps) => { - const [isConfirmVisible, toggleVisibility] = useToggle(false); - - return isConfirmVisible ? ( - - - - - - - - - ) : ( - - ); +const searchConfig = { + box: { incremental: true }, }; -export function SavedViewManageViewsFlyout({ +export function ManageViewsFlyout({ onClose, - views, - setView, + views = [], + onSwitchView, onMakeDefaultView, onDeleteView, loading, - sourceIsLoading, -}: Props) { - const [inProgressView, setInProgressView] = useState(null); +}: ManageViewsFlyoutProps) { + // Add name as top level property to allow in memory search + const namedViews = useMemo(() => views.map(addOwnName), [views]); - const renderName = (name: string, item: SavedView) => ( + const renderName = (name: string, item: View) => ( { - setView(item); + onSwitchView(item.id); onClose(); }} > @@ -95,11 +74,11 @@ export function SavedViewManageViewsFlyout({ ); - const renderDeleteAction = (item: SavedView) => { + const renderDeleteAction = (item: View) => { return ( { onDeleteView(item.id); }} @@ -107,22 +86,21 @@ export function SavedViewManageViewsFlyout({ ); }; - const renderMakeDefaultAction = (item: SavedView) => { + const renderMakeDefaultAction = (item: View) => { return ( - { - setInProgressView(item.id); onMakeDefaultView(item.id); }} /> ); }; - const columns = [ + const columns: Array> = [ { field: 'name', name: i18n.translate('xpack.infra.openView.columnNames.name', { defaultMessage: 'Name' }), @@ -139,7 +117,7 @@ export function SavedViewManageViewsFlyout({ render: renderMakeDefaultAction, }, { - available: (item: SavedView) => item.id !== '0', + available: (item) => !item.attributes.isStatic, render: renderDeleteAction, }, ], @@ -161,10 +139,10 @@ export function SavedViewManageViewsFlyout({ @@ -178,3 +156,41 @@ export function SavedViewManageViewsFlyout({ ); } + +const DeleteConfimation = ({ isDisabled, onConfirm }: DeleteConfimationProps) => { + const [isConfirmVisible, toggleVisibility] = useToggle(false); + + return isConfirmVisible ? ( + + + + + + + + + ) : ( + + ); +}; + +/** + * Helpers + */ +const addOwnName = (view: View) => ({ ...view, name: view.attributes.name }); diff --git a/x-pack/plugins/infra/public/components/saved_views/toolbar_control.tsx b/x-pack/plugins/infra/public/components/saved_views/toolbar_control.tsx index 1610b1b63fd82..b52d83cac60c6 100644 --- a/x-pack/plugins/infra/public/components/saved_views/toolbar_control.tsx +++ b/x-pack/plugins/infra/public/components/saved_views/toolbar_control.tsx @@ -5,132 +5,105 @@ * 2.0. */ -import React, { useCallback, useState, useEffect } from 'react'; +import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButton, EuiPopover, EuiListGroup, EuiListGroupItem } from '@elastic/eui'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; import { FormattedMessage } from '@kbn/i18n-react'; -import { SavedViewManageViewsFlyout } from './manage_views_flyout'; -import { useSavedViewContext } from '../../containers/saved_view/saved_view'; -import { SavedViewListModal } from './view_list_modal'; +import { NonEmptyString } from '@kbn/io-ts-utils'; +import { ManageViewsFlyout } from './manage_views_flyout'; import { useBoolean } from '../../hooks/use_boolean'; import { UpsertViewModal } from './upsert_modal'; - -interface Props { - viewState: ViewState; +import { UseInventoryViewsResult } from '../../hooks/use_inventory_views'; +import { UseMetricsExplorerViewsResult } from '../../hooks/use_metrics_explorer_views'; + +type UseViewProps = + | 'currentView' + | 'views' + | 'isFetchingViews' + | 'isFetchingCurrentView' + | 'isCreatingView' + | 'isUpdatingView'; + +type UseViewResult = UseInventoryViewsResult | UseMetricsExplorerViewsResult; +type InventoryViewsResult = Pick; +type MetricsExplorerViewsResult = Pick; + +interface Props extends InventoryViewsResult, MetricsExplorerViewsResult { + viewState: ViewState & { time?: number }; + onCreateView: UseViewResult['createView']; + onDeleteView: UseViewResult['deleteViewById']; + onUpdateView: UseViewResult['updateViewById']; + onLoadViews: UseViewResult['fetchViews']; + onSetDefaultView: UseViewResult['setDefaultViewById']; + onSwitchView: UseViewResult['switchViewById']; } export function SavedViewsToolbarControls(props: Props) { - const kibana = useKibana(); const { - views, - saveView, - loading, - updateView, - deletedId, - deleteView, - makeDefault, - sourceIsLoading, - find, - errorOnFind, - errorOnCreate, - createdView, - updatedView, currentView, - setCurrentView, - } = useSavedViewContext(); + views, + isFetchingViews, + isFetchingCurrentView, + isCreatingView, + isUpdatingView, + onCreateView, + onDeleteView, + onUpdateView, + onLoadViews, + onSetDefaultView, + onSwitchView, + viewState, + } = props; const [isPopoverOpen, { off: closePopover, toggle: togglePopover }] = useBoolean(false); const [isManageFlyoutOpen, { on: openManageFlyout, off: closeManageFlyout }] = useBoolean(false); - const [isUpdateModalOpen, { on: openUpdateModal, off: closeUpdateModal }] = useBoolean(false); - const [isLoadModalOpen, { on: openLoadModal, off: closeLoadModal }] = useBoolean(false); const [isCreateModalOpen, { on: openCreateModal, off: closeCreateModal }] = useBoolean(false); + const [isUpdateModalOpen, { on: openUpdateModal, off: closeUpdateModal }] = useBoolean(false); - const [isInvalid, setIsInvalid] = useState(false); + const togglePopoverAndLoad = () => { + if (!isPopoverOpen) { + onLoadViews(); + } + togglePopover(); + }; const goToManageViews = () => { closePopover(); - find(); openManageFlyout(); }; - const goToLoadView = () => { - closePopover(); - find(); - openLoadModal(); - }; - const goToCreateView = () => { closePopover(); - setIsInvalid(false); openCreateModal(); }; const goToUpdateView = () => { closePopover(); - setIsInvalid(false); openUpdateModal(); }; - const save = useCallback( - (name: string, hasTime: boolean = false) => { - const currentState = { - ...props.viewState, - ...(!hasTime ? { time: undefined } : {}), - }; - saveView({ ...currentState, name }); - }, - [props.viewState, saveView] - ); - - const update = useCallback( - (name: string, hasTime: boolean = false) => { - const currentState = { - ...props.viewState, - ...(!hasTime ? { time: undefined } : {}), - }; - updateView(currentView.id, { ...currentState, name }); - }, - [props.viewState, updateView, currentView] - ); + const handleCreateView = (name: NonEmptyString, shouldIncludeTime: boolean = false) => { + const attributes = { ...viewState, name }; - useEffect(() => { - if (errorOnCreate) { - setIsInvalid(true); + if (!shouldIncludeTime) { + delete attributes.time; } - }, [errorOnCreate]); - useEffect(() => { - if (updatedView !== undefined) { - setCurrentView(updatedView); - // INFO: Close the modal after the view is created. - closeUpdateModal(); - } - }, [updatedView, setCurrentView, closeUpdateModal]); + onCreateView(attributes).then(closeCreateModal); + }; - useEffect(() => { - if (createdView !== undefined) { - // INFO: Close the modal after the view is created. - setCurrentView(createdView); - closeCreateModal(); - } - }, [createdView, setCurrentView, closeCreateModal]); + const handleUpdateView = (name: NonEmptyString, shouldIncludeTime: boolean = false) => { + if (!currentView) return; - useEffect(() => { - if (deletedId !== undefined) { - // INFO: Refresh view list after an item is deleted - find(); - } - }, [deletedId, find]); + const attributes = { ...viewState, name }; - useEffect(() => { - if (errorOnCreate) { - kibana.notifications.toasts.warning(getErrorToast('create', errorOnCreate)!); - } else if (errorOnFind) { - kibana.notifications.toasts.warning(getErrorToast('find', errorOnFind)!); + if (!shouldIncludeTime) { + delete attributes.time; } - }, [errorOnCreate, errorOnFind, kibana]); + + onUpdateView({ id: currentView.id, attributes }).then(closeUpdateModal); + }; return ( <> @@ -138,14 +111,15 @@ export function SavedViewsToolbarControls(props: Props) { data-test-subj="savedViews-popover" button={ {currentView - ? currentView.name + ? currentView.attributes.name : i18n.translate('xpack.infra.savedView.unknownView', { defaultMessage: 'No view selected', })} @@ -168,19 +142,11 @@ export function SavedViewsToolbarControls(props: Props) { data-test-subj="savedViews-updateView" iconType="refresh" onClick={goToUpdateView} - isDisabled={!currentView || currentView.id === '0'} + isDisabled={!currentView || currentView.attributes.isStatic} label={i18n.translate('xpack.infra.savedView.updateView', { defaultMessage: 'Update view', })} /> - (props: Props) { {isCreateModalOpen && ( (props: Props) { )} {isUpdateModalOpen && ( (props: Props) { } /> )} - {isLoadModalOpen && ( - - currentView={currentView} - views={views} - onClose={closeLoadModal} - setView={setCurrentView} - /> - )} {isManageFlyoutOpen && ( - - sourceIsLoading={sourceIsLoading} - loading={loading} + )} ); } - -const getErrorToast = (type: 'create' | 'find', msg?: string) => { - if (type === 'create') { - return { - toastLifeTimeMs: 3000, - title: - msg || - i18n.translate('xpack.infra.savedView.errorOnCreate.title', { - defaultMessage: `An error occured saving view.`, - }), - }; - } else if (type === 'find') { - return { - toastLifeTimeMs: 3000, - title: - msg || - i18n.translate('xpack.infra.savedView.findError.title', { - defaultMessage: `An error occurred while loading views.`, - }), - }; - } -}; diff --git a/x-pack/plugins/infra/public/components/saved_views/upsert_modal.tsx b/x-pack/plugins/infra/public/components/saved_views/upsert_modal.tsx index fa2fc3777ca90..08476357cec01 100644 --- a/x-pack/plugins/infra/public/components/saved_views/upsert_modal.tsx +++ b/x-pack/plugins/infra/public/components/saved_views/upsert_modal.tsx @@ -19,29 +19,36 @@ import { EuiFieldText, EuiSpacer, EuiSwitch, + EuiSwitchEvent, EuiText, } from '@elastic/eui'; -import { EuiSwitchEvent } from '@elastic/eui'; +import { NonEmptyString } from '@kbn/io-ts-utils'; interface Props { - isInvalid: boolean; onClose(): void; - onSave(name: string, shouldIncludeTime: boolean): void; + onSave(name: NonEmptyString, shouldIncludeTime: boolean): void; + isSaving: boolean; initialName?: string; initialIncludeTime?: boolean; title: React.ReactNode; } +const nameLabel = i18n.translate('xpack.infra.waffle.savedViews.viewNamePlaceholder', { + defaultMessage: 'Name', +}); + export const UpsertViewModal = ({ onClose, onSave, - isInvalid, + isSaving, initialName = '', initialIncludeTime = false, title, }: Props) => { const [viewName, setViewName] = useState(initialName); - const [includeTime, setIncludeTime] = useState(initialIncludeTime); + const [shouldIncludeTime, setIncludeTime] = useState(initialIncludeTime); + + const trimmedName = viewName.trim() as NonEmptyString; const handleNameChange: React.ChangeEventHandler = (e) => { setViewName(e.target.value); @@ -52,7 +59,7 @@ export const UpsertViewModal = ({ }; const saveView = () => { - onSave(viewName, includeTime); + onSave(trimmedName, shouldIncludeTime); }; return ( @@ -62,16 +69,11 @@ export const UpsertViewModal = ({ } - checked={includeTime} + checked={shouldIncludeTime} onChange={handleTimeCheckChange} /> @@ -101,9 +103,9 @@ export const UpsertViewModal = ({ /> diff --git a/x-pack/plugins/infra/public/components/saved_views/view_list_modal.tsx b/x-pack/plugins/infra/public/components/saved_views/view_list_modal.tsx deleted file mode 100644 index 43ca2776b284d..0000000000000 --- a/x-pack/plugins/infra/public/components/saved_views/view_list_modal.tsx +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useCallback, useState, useMemo } from 'react'; - -import { EuiButtonEmpty, EuiModalFooter, EuiButton, EuiSpacer } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiModal, EuiModalHeader, EuiModalHeaderTitle, EuiModalBody } from '@elastic/eui'; -import { EuiSelectable } from '@elastic/eui'; -import { EuiSelectableOption } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { SavedView } from '../../containers/saved_view/saved_view'; - -interface Props { - views: Array>; - onClose(): void; - setView(viewState: ViewState): void; - currentView?: ViewState; -} - -export function SavedViewListModal({ - onClose, - views, - setView, - currentView, -}: Props) { - const [options, setOptions] = useState(null); - - const onChange = useCallback((opts: EuiSelectableOption[]) => { - setOptions(opts); - }, []); - - const loadView = useCallback(() => { - if (!options) { - onClose(); - return; - } - - const selected = options.find((o) => o.checked); - if (!selected) { - onClose(); - return; - } - setView(views.find((v) => v.id === selected.key)!); - onClose(); - }, [options, views, setView, onClose]); - - const defaultOptions = useMemo(() => { - return views.map((v) => ({ - label: v.name, - key: v.id, - checked: currentView?.id === v.id ? 'on' : undefined, - })); - }, [views, currentView]); - - return ( - - - - - - - - - {(list, search) => ( - <> - {search} - - {list} - - )} - - - - - - - - - - - - ); -} diff --git a/x-pack/plugins/infra/public/containers/react_query_provider.tsx b/x-pack/plugins/infra/public/containers/react_query_provider.tsx new file mode 100644 index 0000000000000..050575dcc054a --- /dev/null +++ b/x-pack/plugins/infra/public/containers/react_query_provider.tsx @@ -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 React, { useState } from 'react'; +import { QueryClient, QueryClientConfig, QueryClientProvider } from '@tanstack/react-query'; +import merge from 'lodash/merge'; +import { EuiButtonIcon } from '@elastic/eui'; +import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; + +const DEFAULT_CONFIG = { + defaultOptions: { + queries: { keepPreviousData: true, refetchOnWindowFocus: false }, + }, +}; + +interface ProviderProps { + children: React.ReactNode; + config?: QueryClientConfig; +} + +export function ReactQueryProvider({ children, config = {} }: ProviderProps) { + const [queryClient] = useState(() => new QueryClient(merge(DEFAULT_CONFIG, config))); + + return ( + + + {children} + + ); +} + +function HideableReactQueryDevTools() { + const [isHidden, setIsHidden] = useState(false); + + return !isHidden ? ( +
+ setIsHidden(!isHidden)} + aria-label="Disable React Query Dev Tools" + /> + +
+ ) : null; +} diff --git a/x-pack/plugins/infra/public/hooks/use_inventory_views.ts b/x-pack/plugins/infra/public/hooks/use_inventory_views.ts new file mode 100644 index 0000000000000..a75e8beaf3f65 --- /dev/null +++ b/x-pack/plugins/infra/public/hooks/use_inventory_views.ts @@ -0,0 +1,262 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 rt from 'io-ts'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { fold } from 'fp-ts/lib/Either'; +import { constant, identity } from 'fp-ts/lib/function'; + +import { + QueryObserverBaseResult, + UseMutateAsyncFunction, + UseMutateFunction, + useMutation, + useQuery, + useQueryClient, +} from '@tanstack/react-query'; +import { useUiTracker } from '@kbn/observability-plugin/public'; + +import { IHttpFetchError, ResponseErrorBody } from '@kbn/core-http-browser'; +import { MetricsSourceConfigurationResponse } from '../../common/metrics_sources'; +import { + CreateInventoryViewAttributesRequestPayload, + UpdateInventoryViewAttributesRequestPayload, +} from '../../common/http_api/latest'; +import type { InventoryView } from '../../common/inventory_views'; +import { useKibanaContextForPlugin } from './use_kibana'; +import { useUrlState } from '../utils/use_url_state'; +import { useSavedViewsNotifier } from './use_saved_views_notifier'; +import { useSourceContext } from '../containers/metrics_source'; + +interface UpdateViewParams { + id: string; + attributes: UpdateInventoryViewAttributesRequestPayload; +} + +export interface UseInventoryViewsResult { + views?: InventoryView[]; + currentView?: InventoryView | null; + createView: UseMutateAsyncFunction< + InventoryView, + ServerError, + CreateInventoryViewAttributesRequestPayload + >; + deleteViewById: UseMutateFunction; + fetchViews: QueryObserverBaseResult['refetch']; + updateViewById: UseMutateAsyncFunction; + switchViewById: (id: InventoryViewId) => void; + setDefaultViewById: UseMutateFunction< + MetricsSourceConfigurationResponse, + ServerError, + string, + MutationContext + >; + isCreatingView: boolean; + isFetchingCurrentView: boolean; + isFetchingViews: boolean; + isUpdatingView: boolean; +} + +type ServerError = IHttpFetchError; + +interface MutationContext { + id?: string; + previousViews?: InventoryView[]; +} + +const queryKeys = { + find: ['inventory-views-find'] as const, + get: ['inventory-views-get'] as const, + getById: (id: string) => ['inventory-views-get', id] as const, +}; + +export const useInventoryViews = (): UseInventoryViewsResult => { + const { inventoryViews } = useKibanaContextForPlugin().services; + const trackMetric = useUiTracker({ app: 'infra_metrics' }); + + const queryClient = useQueryClient(); + const { source, updateSourceConfiguration } = useSourceContext(); + + const defaultViewId = source?.configuration.inventoryDefaultView ?? '0'; + + const [currentViewId, switchViewById] = useUrlState({ + defaultState: defaultViewId, + decodeUrlState, + encodeUrlState, + urlStateKey: 'inventoryViewId', + writeDefaultState: true, + }); + + const notify = useSavedViewsNotifier(); + + const { + data: views, + refetch: fetchViews, + isFetching: isFetchingViews, + } = useQuery({ + queryKey: queryKeys.find, + queryFn: () => inventoryViews.client.findInventoryViews(), + enabled: false, // We will manually fetch the list when necessary + placeholderData: [], // Use a default empty array instead of undefined + onError: (error: ServerError) => notify.getViewFailure(error.body?.message ?? error.message), + onSuccess: (data) => { + const prefix = data.length >= 1000 ? 'over' : 'under'; + trackMetric({ metric: `${prefix}_1000_saved_objects_for_inventory_view` }); + }, + }); + + const { data: currentView, isFetching: isFetchingCurrentView } = useQuery({ + queryKey: queryKeys.getById(currentViewId), + queryFn: ({ queryKey: [, id] }) => inventoryViews.client.getInventoryView(id), + onError: (error: ServerError) => { + notify.getViewFailure(error.body?.message ?? error.message); + switchViewById(defaultViewId); + }, + placeholderData: null, + }); + + const { mutate: setDefaultViewById } = useMutation< + MetricsSourceConfigurationResponse, + ServerError, + string, + MutationContext + >({ + mutationFn: (id) => updateSourceConfiguration({ inventoryDefaultView: id }), + /** + * To provide a quick feedback, we perform an optimistic update on the list + * when updating the default view. + * 1. Cancel any outgoing refetches (so they don't overwrite our optimistic update) + * 2. Snapshot the previous views list + * 3. Optimistically update the list with new default view and store in cache + * 4. Return a context object with the snapshotted views + */ + onMutate: async (id) => { + await queryClient.cancelQueries({ queryKey: queryKeys.find }); // 1 + const previousViews = queryClient.getQueryData(queryKeys.find); // 2 + const updatedViews = getListWithUpdatedDefault(id, previousViews); // 3 + queryClient.setQueryData(queryKeys.find, updatedViews); + return { previousViews }; // 4 + }, + // If the mutation fails but doesn't retrigger the error, use the context returned from onMutate to roll back + onSuccess: (data, _id, context) => { + if (!data && context?.previousViews) { + return queryClient.setQueryData(queryKeys.find, context.previousViews); + } + return queryClient.invalidateQueries({ queryKey: queryKeys.get }); + }, + }); + + const { mutateAsync: createView, isLoading: isCreatingView } = useMutation< + InventoryView, + ServerError, + CreateInventoryViewAttributesRequestPayload + >({ + mutationFn: (attributes) => inventoryViews.client.createInventoryView(attributes), + onError: (error) => { + notify.upsertViewFailure(error.body?.message ?? error.message); + }, + onSuccess: (createdView) => { + queryClient.setQueryData(queryKeys.getById(createdView.id), createdView); // Store in cache created view + switchViewById(createdView.id); // Update current view and url state + }, + }); + + const { mutateAsync: updateViewById, isLoading: isUpdatingView } = useMutation< + InventoryView, + ServerError, + UpdateViewParams + >({ + mutationFn: ({ id, attributes }) => inventoryViews.client.updateInventoryView(id, attributes), + onError: (error) => { + notify.upsertViewFailure(error.body?.message ?? error.message); + }, + onSuccess: (updatedView) => { + queryClient.setQueryData(queryKeys.getById(updatedView.id), updatedView); // Store in cache updated view + }, + }); + + const { mutate: deleteViewById } = useMutation({ + mutationFn: (id: string) => inventoryViews.client.deleteInventoryView(id), + /** + * To provide a quick feedback, we perform an optimistic update on the list + * when deleting a view. + * 1. Cancel any outgoing refetches (so they don't overwrite our optimistic update) + * 2. Snapshot the previous views list + * 3. Optimistically update the list removing the view and store in cache + * 4. Return a context object with the snapshotted views + */ + onMutate: async (id) => { + await queryClient.cancelQueries({ queryKey: queryKeys.find }); // 1 + + const previousViews = queryClient.getQueryData(queryKeys.find); // 2 + + const updatedViews = getListWithoutDeletedView(id, previousViews); // 3 + queryClient.setQueryData(queryKeys.find, updatedViews); + + return { previousViews }; // 4 + }, + // If the mutation fails, use the context returned from onMutate to roll back + onError: (error, _id, context) => { + notify.deleteViewFailure(error.body?.message ?? error.message); + if (context?.previousViews) { + queryClient.setQueryData(queryKeys.find, context.previousViews); + } + }, + onSuccess: (_data, id) => { + // If the deleted view was the current one, switch to the default view + if (currentView?.id === id) { + switchViewById(defaultViewId); + } + }, + onSettled: () => { + fetchViews(); // Invalidate views list cache and refetch views + }, + }); + + return { + // Values + views, + currentView, + // Actions about updating view + createView, + deleteViewById, + fetchViews, + updateViewById, + switchViewById, + setDefaultViewById, + // Status flags + isCreatingView, + isFetchingCurrentView, + isFetchingViews, + isUpdatingView, + }; +}; + +const inventoryViewIdRT = rt.string; +type InventoryViewId = rt.TypeOf; + +const encodeUrlState = inventoryViewIdRT.encode; +const decodeUrlState = (value: unknown) => { + const state = pipe(inventoryViewIdRT.decode(value), fold(constant(undefined), identity)); + return state; +}; + +/** + * Helpers + */ +const getListWithUpdatedDefault = (id: string, views: InventoryView[] = []) => { + return views.map((view) => ({ + ...view, + attributes: { + ...view.attributes, + isDefault: view.id === id, + }, + })); +}; + +const getListWithoutDeletedView = (id: string, views: InventoryView[] = []) => { + return views.filter((view) => view.id !== id); +}; diff --git a/x-pack/plugins/infra/public/hooks/use_lens_attributes.ts b/x-pack/plugins/infra/public/hooks/use_lens_attributes.ts index 6250d20750e29..322ab47b7f459 100644 --- a/x-pack/plugins/infra/public/hooks/use_lens_attributes.ts +++ b/x-pack/plugins/infra/public/hooks/use_lens_attributes.ts @@ -74,7 +74,13 @@ export const useLensAttributes = ({ return visualizationAttributes; }, [dataView, formulaAPI, options, type, visualizationType]); - const injectFilters = (data: { filters: Filter[]; query: Query }): LensAttributes | null => { + const injectFilters = ({ + filters, + query = { language: 'kuery', query: '' }, + }: { + filters: Filter[]; + query?: Query; + }): LensAttributes | null => { if (!attributes) { return null; } @@ -82,8 +88,8 @@ export const useLensAttributes = ({ ...attributes, state: { ...attributes.state, - query: data.query, - filters: [...attributes.state.filters, ...data.filters], + query, + filters: [...attributes.state.filters, ...filters], }, }; }; @@ -95,7 +101,7 @@ export const useLensAttributes = ({ }: { timeRange: TimeRange; filters: Filter[]; - query: Query; + query?: Query; }) => { return { openInLens: { diff --git a/x-pack/plugins/infra/public/hooks/use_metrics_explorer_views.ts b/x-pack/plugins/infra/public/hooks/use_metrics_explorer_views.ts new file mode 100644 index 0000000000000..34a3666b5c0e0 --- /dev/null +++ b/x-pack/plugins/infra/public/hooks/use_metrics_explorer_views.ts @@ -0,0 +1,263 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import * as rt from 'io-ts'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { fold } from 'fp-ts/lib/Either'; +import { constant, identity } from 'fp-ts/lib/function'; + +import { + QueryObserverBaseResult, + UseMutateAsyncFunction, + UseMutateFunction, + useMutation, + useQuery, + useQueryClient, +} from '@tanstack/react-query'; +import { useUiTracker } from '@kbn/observability-plugin/public'; + +import { IHttpFetchError, ResponseErrorBody } from '@kbn/core-http-browser'; +import { MetricsSourceConfigurationResponse } from '../../common/metrics_sources'; +import { + CreateMetricsExplorerViewAttributesRequestPayload, + UpdateMetricsExplorerViewAttributesRequestPayload, +} from '../../common/http_api/latest'; +import { MetricsExplorerView } from '../../common/metrics_explorer_views'; +import { useKibanaContextForPlugin } from './use_kibana'; +import { useUrlState } from '../utils/use_url_state'; +import { useSavedViewsNotifier } from './use_saved_views_notifier'; +import { useSourceContext } from '../containers/metrics_source'; + +interface UpdateViewParams { + id: string; + attributes: UpdateMetricsExplorerViewAttributesRequestPayload; +} + +export interface UseMetricsExplorerViewsResult { + views?: MetricsExplorerView[]; + currentView?: MetricsExplorerView | null; + createView: UseMutateAsyncFunction< + MetricsExplorerView, + ServerError, + CreateMetricsExplorerViewAttributesRequestPayload + >; + deleteViewById: UseMutateFunction; + fetchViews: QueryObserverBaseResult['refetch']; + updateViewById: UseMutateAsyncFunction; + switchViewById: (id: MetricsExplorerViewId) => void; + setDefaultViewById: UseMutateFunction< + MetricsSourceConfigurationResponse, + ServerError, + string, + MutationContext + >; + isCreatingView: boolean; + isFetchingCurrentView: boolean; + isFetchingViews: boolean; + isUpdatingView: boolean; +} + +type ServerError = IHttpFetchError; + +interface MutationContext { + id?: string; + previousViews?: MetricsExplorerView[]; +} + +const queryKeys = { + find: ['metrics-explorer-views-find'] as const, + get: ['metrics-explorer-views-get'] as const, + getById: (id: string) => ['metrics-explorer-views-get', id] as const, +}; + +export const useMetricsExplorerViews = (): UseMetricsExplorerViewsResult => { + const { metricsExplorerViews } = useKibanaContextForPlugin().services; + const trackMetric = useUiTracker({ app: 'infra_metrics' }); + + const queryClient = useQueryClient(); + const { source, updateSourceConfiguration } = useSourceContext(); + + const defaultViewId = source?.configuration.metricsExplorerDefaultView ?? '0'; + + const [currentViewId, switchViewById] = useUrlState({ + defaultState: defaultViewId, + decodeUrlState, + encodeUrlState, + urlStateKey: 'metricsExplorerViewId', + writeDefaultState: true, + }); + + const notify = useSavedViewsNotifier(); + + const { + data: views, + refetch: fetchViews, + isFetching: isFetchingViews, + } = useQuery({ + queryKey: queryKeys.find, + queryFn: () => metricsExplorerViews.client.findMetricsExplorerViews(), + enabled: false, // We will manually fetch the list when necessary + placeholderData: [], // Use a default empty array instead of undefined + onError: (error: ServerError) => notify.getViewFailure(error.body?.message ?? error.message), + onSuccess: (data) => { + const prefix = data.length >= 1000 ? 'over' : 'under'; + trackMetric({ metric: `${prefix}_1000_saved_objects_for_metrics_explorer_view` }); + }, + }); + + const { data: currentView, isFetching: isFetchingCurrentView } = useQuery({ + queryKey: queryKeys.getById(currentViewId), + queryFn: ({ queryKey: [, id] }) => metricsExplorerViews.client.getMetricsExplorerView(id), + onError: (error: ServerError) => { + notify.getViewFailure(error.body?.message ?? error.message); + switchViewById(defaultViewId); + }, + placeholderData: null, + }); + + const { mutate: setDefaultViewById } = useMutation< + MetricsSourceConfigurationResponse, + ServerError, + string, + MutationContext + >({ + mutationFn: (id) => updateSourceConfiguration({ metricsExplorerDefaultView: id }), + /** + * To provide a quick feedback, we perform an optimistic update on the list + * when updating the default view. + * 1. Cancel any outgoing refetches (so they don't overwrite our optimistic update) + * 2. Snapshot the previous views list + * 3. Optimistically update the list with new default view and store in cache + * 4. Return a context object with the snapshotted views + */ + onMutate: async (id) => { + await queryClient.cancelQueries({ queryKey: queryKeys.find }); // 1 + const previousViews = queryClient.getQueryData(queryKeys.find); // 2 + const updatedViews = getListWithUpdatedDefault(id, previousViews); // 3 + queryClient.setQueryData(queryKeys.find, updatedViews); + return { previousViews }; // 4 + }, + // If the mutation fails but doesn't retrigger the error, use the context returned from onMutate to roll back + onSuccess: (data, _id, context) => { + if (!data && context?.previousViews) { + return queryClient.setQueryData(queryKeys.find, context.previousViews); + } + return queryClient.invalidateQueries({ queryKey: queryKeys.get }); + }, + }); + + const { mutateAsync: createView, isLoading: isCreatingView } = useMutation< + MetricsExplorerView, + ServerError, + CreateMetricsExplorerViewAttributesRequestPayload + >({ + mutationFn: (attributes) => metricsExplorerViews.client.createMetricsExplorerView(attributes), + onError: (error) => { + notify.upsertViewFailure(error.body?.message ?? error.message); + }, + onSuccess: (createdView) => { + queryClient.setQueryData(queryKeys.getById(createdView.id), createdView); // Store in cache created view + switchViewById(createdView.id); // Update current view and url state + }, + }); + + const { mutateAsync: updateViewById, isLoading: isUpdatingView } = useMutation< + MetricsExplorerView, + ServerError, + UpdateViewParams + >({ + mutationFn: ({ id, attributes }) => + metricsExplorerViews.client.updateMetricsExplorerView(id, attributes), + onError: (error) => { + notify.upsertViewFailure(error.body?.message ?? error.message); + }, + onSuccess: (updatedView) => { + queryClient.setQueryData(queryKeys.getById(updatedView.id), updatedView); // Store in cache updated view + }, + }); + + const { mutate: deleteViewById } = useMutation({ + mutationFn: (id: string) => metricsExplorerViews.client.deleteMetricsExplorerView(id), + /** + * To provide a quick feedback, we perform an optimistic update on the list + * when deleting a view. + * 1. Cancel any outgoing refetches (so they don't overwrite our optimistic update) + * 2. Snapshot the previous views list + * 3. Optimistically update the list removing the view and store in cache + * 4. Return a context object with the snapshotted views + */ + onMutate: async (id) => { + await queryClient.cancelQueries({ queryKey: queryKeys.find }); // 1 + + const previousViews = queryClient.getQueryData(queryKeys.find); // 2 + + const updatedViews = getListWithoutDeletedView(id, previousViews); // 3 + queryClient.setQueryData(queryKeys.find, updatedViews); + + return { previousViews }; // 4 + }, + // If the mutation fails, use the context returned from onMutate to roll back + onError: (error, _id, context) => { + notify.deleteViewFailure(error.body?.message ?? error.message); + if (context?.previousViews) { + queryClient.setQueryData(queryKeys.find, context.previousViews); + } + }, + onSuccess: (_data, id) => { + // If the deleted view was the current one, switch to the default view + if (currentView?.id === id) { + switchViewById(defaultViewId); + } + }, + onSettled: () => { + fetchViews(); // Invalidate views list cache and refetch views + }, + }); + + return { + // Values + views, + currentView, + // Actions about updating view + createView, + deleteViewById, + fetchViews, + updateViewById, + switchViewById, + setDefaultViewById, + // Status flags + isCreatingView, + isFetchingCurrentView, + isFetchingViews, + isUpdatingView, + }; +}; + +const metricsExplorerViewIdRT = rt.string; +type MetricsExplorerViewId = rt.TypeOf; + +const encodeUrlState = metricsExplorerViewIdRT.encode; +const decodeUrlState = (value: unknown) => { + const state = pipe(metricsExplorerViewIdRT.decode(value), fold(constant(undefined), identity)); + return state; +}; + +/** + * Helpers + */ +const getListWithUpdatedDefault = (id: string, views: MetricsExplorerView[] = []) => { + return views.map((view) => ({ + ...view, + attributes: { + ...view.attributes, + isDefault: view.id === id, + }, + })); +}; + +const getListWithoutDeletedView = (id: string, views: MetricsExplorerView[] = []) => { + return views.filter((view) => view.id !== id); +}; diff --git a/x-pack/plugins/infra/public/hooks/use_saved_views_notifier.ts b/x-pack/plugins/infra/public/hooks/use_saved_views_notifier.ts new file mode 100644 index 0000000000000..b7b8822e3ec14 --- /dev/null +++ b/x-pack/plugins/infra/public/hooks/use_saved_views_notifier.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { useKibanaContextForPlugin } from './use_kibana'; + +export const useSavedViewsNotifier = () => { + const { notifications } = useKibanaContextForPlugin(); + + const deleteViewFailure = (message?: string) => { + notifications.toasts.danger({ + toastLifeTimeMs: 3000, + title: + message || + i18n.translate('xpack.infra.savedView.errorOnDelete.title', { + defaultMessage: `An error occured deleting the view.`, + }), + }); + }; + + const getViewFailure = (message?: string) => { + notifications.toasts.danger({ + toastLifeTimeMs: 3000, + title: + message || + i18n.translate('xpack.infra.savedView.findError.title', { + defaultMessage: `An error occurred while loading views.`, + }), + }); + }; + + const upsertViewFailure = (message?: string) => { + notifications.toasts.danger({ + toastLifeTimeMs: 3000, + title: + message || + i18n.translate('xpack.infra.savedView.errorOnCreate.title', { + defaultMessage: `An error occured saving view.`, + }), + }); + }; + + return { + deleteViewFailure, + getViewFailure, + upsertViewFailure, + }; +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/chart/lens_wrapper.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/chart/lens_wrapper.tsx index 34e536aaf37d2..2355812e277d5 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/chart/lens_wrapper.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/chart/lens_wrapper.tsx @@ -19,7 +19,7 @@ export interface LensWrapperProps { id: string; attributes: LensAttributes | null; dateRange: TimeRange; - query: Query; + query?: Query; filters: Filter[]; extraActions: Action[]; lastReloadRequestTime?: number; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/tile.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/tile.tsx index 89eebeefd240e..afb1181103d9d 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/tile.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/kpis/tile.tsx @@ -77,22 +77,18 @@ export const Tile = ({ visualizationType: 'metricChart', }); - const hostsFilterQuery = useMemo(() => { - return createHostsFilter( - hostNodes.map((p) => p.name), - dataView - ); + const filters = useMemo(() => { + return [ + createHostsFilter( + hostNodes.map((p) => p.name), + dataView + ), + ]; }, [hostNodes, dataView]); - const filters = useMemo( - () => [...searchCriteria.filters, ...searchCriteria.panelFilters, ...[hostsFilterQuery]], - [hostsFilterQuery, searchCriteria.filters, searchCriteria.panelFilters] - ); - const extraActionOptions = getExtraActions({ timeRange: searchCriteria.dateRange, filters, - query: searchCriteria.query, }); const handleBrushEnd = ({ range }: BrushTriggerEvent['data']) => { @@ -156,7 +152,6 @@ export const Tile = ({ lastReloadRequestTime={afterLoadedState.lastReloadRequestTime} dateRange={afterLoadedState.dateRange} filters={afterLoadedState.filters} - query={afterLoadedState.query} onBrushEnd={handleBrushEnd} loading={loading} /> diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metric_chart.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metric_chart.tsx index f81228957107a..1ef66e96f8abc 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metric_chart.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metric_chart.tsx @@ -60,22 +60,18 @@ export const MetricChart = ({ title, type, breakdownSize }: MetricChartProps) => visualizationType: 'lineChart', }); - const hostsFilterQuery = useMemo(() => { - return createHostsFilter( - currentPage.map((p) => p.name), - dataView - ); + const filters = useMemo(() => { + return [ + createHostsFilter( + currentPage.map((p) => p.name), + dataView + ), + ]; }, [currentPage, dataView]); - const filters = [ - ...afterLoadedState.filters, - ...afterLoadedState.panelFilters, - ...[hostsFilterQuery], - ]; const extraActionOptions = getExtraActions({ timeRange: afterLoadedState.dateRange, filters, - query: afterLoadedState.query, }); const extraActions: Action[] = [extraActionOptions.openInLens]; @@ -132,7 +128,6 @@ export const MetricChart = ({ title, type, breakdownSize }: MetricChartProps) => lastReloadRequestTime={afterLoadedState.lastReloadRequestTime} dateRange={afterLoadedState.dateRange} filters={filters} - query={afterLoadedState.query} onBrushEnd={handleBrushEnd} loading={loading} hasTitle diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx index 7f3dac7a3af16..b61ba46aa2c20 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx @@ -64,7 +64,7 @@ const CHARTS_IN_ORDER: Array & { fullRo export const MetricsGrid = React.memo(() => { return ( - + {CHARTS_IN_ORDER.map(({ fullRow, ...chartProp }) => ( diff --git a/x-pack/plugins/infra/public/pages/metrics/index.tsx b/x-pack/plugins/infra/public/pages/metrics/index.tsx index 37f2a8bbbf01e..df66dc5eb88ba 100644 --- a/x-pack/plugins/infra/public/pages/metrics/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/index.tsx @@ -10,8 +10,6 @@ import { i18n } from '@kbn/i18n'; import React, { useContext } from 'react'; import { RouteComponentProps, Switch } from 'react-router-dom'; import { Route } from '@kbn/shared-ux-router'; -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { EuiErrorBoundary, EuiHeaderLinks, EuiHeaderLink } from '@elastic/eui'; import { useKibana } from '@kbn/kibana-react-plugin/public'; @@ -20,11 +18,7 @@ import { useLinkProps } from '@kbn/observability-plugin/public'; import { MetricsSourceConfigurationProperties } from '../../../common/metrics_sources'; import { HelpCenterContent } from '../../components/help_center_content'; import { useReadOnlyBadge } from '../../hooks/use_readonly_badge'; -import { - MetricsExplorerOptionsContainer, - useMetricsExplorerOptionsContainerContext, - DEFAULT_METRICS_EXPLORER_VIEW_STATE, -} from './metrics_explorer/hooks/use_metrics_explorer_options'; +import { MetricsExplorerOptionsContainer } from './metrics_explorer/hooks/use_metrics_explorer_options'; import { WithMetricsExplorerOptionsUrlState } from '../../containers/metrics_explorer/with_metrics_explorer_options_url_state'; import { MetricsExplorerPage } from './metrics_explorer'; import { SnapshotPage } from './inventory_view'; @@ -36,13 +30,13 @@ import { WaffleOptionsProvider } from './inventory_view/hooks/use_waffle_options import { WaffleTimeProvider } from './inventory_view/hooks/use_waffle_time'; import { WaffleFiltersProvider } from './inventory_view/hooks/use_waffle_filters'; import { MetricsAlertDropdown } from '../../alerting/common/components/metrics_alert_dropdown'; -import { SavedViewProvider } from '../../containers/saved_view/saved_view'; import { AlertPrefillProvider } from '../../alerting/use_alert_prefill'; import { InfraMLCapabilitiesProvider } from '../../containers/ml/infra_ml_capabilities'; import { AnomalyDetectionFlyout } from './inventory_view/components/ml/anomaly_detection/anomaly_detection_flyout'; import { HeaderActionMenuContext } from '../../utils/header_action_menu_provider'; import { CreateDerivedIndexPattern, useSourceContext } from '../../containers/metrics_source'; import { NotFoundPage } from '../404'; +import { ReactQueryProvider } from '../../containers/react_query_provider'; const ADD_DATA_LABEL = i18n.translate('xpack.infra.metricsHeaderAddDataButtonLabel', { defaultMessage: 'Add data', @@ -51,7 +45,6 @@ const ADD_DATA_LABEL = i18n.translate('xpack.infra.metricsHeaderAddDataButtonLab export const InfrastructurePage = ({ match }: RouteComponentProps) => { const uiCapabilities = useKibana().services.application?.capabilities; const { setHeaderActionMenu, theme$ } = useContext(HeaderActionMenuContext); - const queryClient = new QueryClient(); const settingsTabTitle = i18n.translate('xpack.infra.metrics.settingsTabTitle', { defaultMessage: 'Settings', @@ -74,8 +67,7 @@ export const InfrastructurePage = ({ match }: RouteComponentProps) => { - - + { } /> - + @@ -136,19 +128,12 @@ const PageContent = (props: { createDerivedIndexPattern: CreateDerivedIndexPattern; }) => { const { createDerivedIndexPattern, configuration } = props; - const { options } = useMetricsExplorerOptionsContainerContext(); return ( - - - + ); }; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx index 373a6a563fee9..9827c866b1424 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx @@ -36,7 +36,6 @@ import { LegendControls } from './waffle/legend_controls'; import { TryItButton } from '../../../../components/try_it_button'; interface Props { - shouldLoadDefault: boolean; currentView: SavedView | null; reload: () => Promise; interval: string; @@ -52,186 +51,173 @@ interface LegendControlOptions { const HOSTS_LINK_LOCAL_STORAGE_KEY = 'inventoryUI:hostsLinkClicked'; -export const Layout = React.memo( - ({ shouldLoadDefault, currentView, reload, interval, nodes, loading }: Props) => { - const [showLoading, setShowLoading] = useState(true); - const { - metric, - groupBy, - sort, - nodeType, - changeView, - view, - autoBounds, - boundsOverride, - legend, - changeBoundsOverride, - changeAutoBounds, - changeLegend, - } = useWaffleOptionsContext(); - const { currentTime, jumpToTime, isAutoReloading } = useWaffleTimeContext(); - const { applyFilterQuery } = useWaffleFiltersContext(); - const legendPalette = legend?.palette ?? DEFAULT_LEGEND.palette; - const legendSteps = legend?.steps ?? DEFAULT_LEGEND.steps; - const legendReverseColors = legend?.reverseColors ?? DEFAULT_LEGEND.reverseColors; - - const [hostsLinkClicked, setHostsLinkClicked] = useLocalStorage( - HOSTS_LINK_LOCAL_STORAGE_KEY, - false - ); - const hostsLinkClickedRef = useRef(hostsLinkClicked); - - const options = { - formatter: InfraFormatterType.percent, - formatTemplate: '{{value}}', - legend: createLegend(legendPalette, legendSteps, legendReverseColors), - metric, - sort, - groupBy, - }; - - useInterval( - () => { - if (!loading) { - jumpToTime(Date.now()); - } - }, - isAutoReloading ? 5000 : null - ); - - const dataBounds = calculateBoundsFromNodes(nodes); - const bounds = autoBounds ? dataBounds : boundsOverride; - /* eslint-disable-next-line react-hooks/exhaustive-deps */ - const formatter = useCallback(createInventoryMetricFormatter(options.metric), [options.metric]); - const { onViewChange } = useWaffleViewState(); - - useEffect(() => { - if (currentView) { - onViewChange(currentView); +export const Layout = React.memo(({ currentView, reload, interval, nodes, loading }: Props) => { + const [showLoading, setShowLoading] = useState(true); + const { + metric, + groupBy, + sort, + nodeType, + changeView, + view, + autoBounds, + boundsOverride, + legend, + changeBoundsOverride, + changeAutoBounds, + changeLegend, + } = useWaffleOptionsContext(); + const { currentTime, jumpToTime, isAutoReloading } = useWaffleTimeContext(); + const { applyFilterQuery } = useWaffleFiltersContext(); + const legendPalette = legend?.palette ?? DEFAULT_LEGEND.palette; + const legendSteps = legend?.steps ?? DEFAULT_LEGEND.steps; + const legendReverseColors = legend?.reverseColors ?? DEFAULT_LEGEND.reverseColors; + + const [hostsLinkClicked, setHostsLinkClicked] = useLocalStorage( + HOSTS_LINK_LOCAL_STORAGE_KEY, + false + ); + const hostsLinkClickedRef = useRef(hostsLinkClicked); + + const options = { + formatter: InfraFormatterType.percent, + formatTemplate: '{{value}}', + legend: createLegend(legendPalette, legendSteps, legendReverseColors), + metric, + sort, + groupBy, + }; + + useInterval( + () => { + if (!loading) { + jumpToTime(Date.now()); } - }, [currentView, onViewChange]); - - useEffect(() => { - // load snapshot data after default view loaded, unless we're not loading a view - if (currentView != null || !shouldLoadDefault) { - reload(); - } - - /** - * INFO: why disable exhaustive-deps - * We need to wait on the currentView not to be null because it is loaded async and could change the view state. - * We don't actually need to watch the value of currentView though, since the view state will be synched up by the - * changing params in the reload method so we should only "watch" the reload method. - * - * TODO: Should refactor this in the future to make it more clear where all the view state is coming - * from and it's precedence [query params, localStorage, defaultView, out of the box view] - */ - /* eslint-disable-next-line react-hooks/exhaustive-deps */ - }, [reload, shouldLoadDefault]); - - useEffect(() => { - setShowLoading(true); - }, [options.metric, nodeType]); - - useEffect(() => { - const hasNodes = nodes && nodes.length; - // Don't show loading screen when we're auto-reloading - setShowLoading(!hasNodes); - }, [nodes]); - - const handleLegendControlChange = useCallback( - (opts: LegendControlOptions) => { - changeBoundsOverride(opts.bounds); - changeAutoBounds(opts.auto); - changeLegend(opts.legend); - }, - [changeBoundsOverride, changeAutoBounds, changeLegend] - ); - - return ( - <> - - - - - - - {view === 'map' && ( - - - - )} + }, + isAutoReloading ? 5000 : null + ); + + const dataBounds = calculateBoundsFromNodes(nodes); + const bounds = autoBounds ? dataBounds : boundsOverride; + /* eslint-disable-next-line react-hooks/exhaustive-deps */ + const formatter = useCallback(createInventoryMetricFormatter(options.metric), [options.metric]); + const { onViewChange } = useWaffleViewState(); + + useEffect(() => { + if (currentView) { + onViewChange(currentView); + } + }, [currentView, onViewChange]); + + useEffect(() => { + // load snapshot data after default view loaded, unless we're not loading a view + if (currentView != null) { + reload(); + } + }, [currentView, reload]); + + useEffect(() => { + setShowLoading(true); + }, [options.metric, nodeType]); + + useEffect(() => { + const hasNodes = nodes && nodes.length; + // Don't show loading screen when we're auto-reloading + setShowLoading(!hasNodes); + }, [nodes]); + + const handleLegendControlChange = useCallback( + (opts: LegendControlOptions) => { + changeBoundsOverride(opts.bounds); + changeAutoBounds(opts.auto); + changeLegend(opts.legend); + }, + [changeBoundsOverride, changeAutoBounds, changeLegend] + ); + + return ( + <> + + + + + + + {view === 'map' && ( - + - + )} + + + - - - {!hostsLinkClickedRef.current && nodeType === 'host' && ( - { - setHostsLinkClicked(true); - }} + + + + {!hostsLinkClickedRef.current && nodeType === 'host' && ( + { + setHostsLinkClicked(true); + }} + /> + )} + + + + {({ bounds: { height = 0 } }) => ( + )} - - - - {({ bounds: { height = 0 } }) => ( - - )} - - - - - - - ); - } -); + + + + + + + ); +}); const TopActionContainer = euiStyled(EuiFlexItem)` padding: ${(props) => `${props.theme.eui.euiSizeM} 0`}; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout_view.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout_view.tsx index af9c9ab5e2b30..f2ea500a98fd1 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout_view.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout_view.tsx @@ -6,8 +6,8 @@ */ import React from 'react'; +import { useInventoryViews } from '../../../../hooks/use_inventory_views'; import { SnapshotNode } from '../../../../../common/http_api'; -import { useSavedViewContext } from '../../../../containers/saved_view/saved_view'; import { Layout } from './layout'; interface Props { @@ -18,6 +18,6 @@ interface Props { } export const LayoutView = (props: Props) => { - const { shouldLoadDefault, currentView } = useSavedViewContext(); - return ; + const { currentView } = useInventoryViews(); + return ; }; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/saved_views.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/saved_views.tsx index 484c0a38f4d06..4547b0dbb0147 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/saved_views.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/saved_views.tsx @@ -6,10 +6,42 @@ */ import React from 'react'; +import { useInventoryViews } from '../../../../hooks/use_inventory_views'; import { SavedViewsToolbarControls } from '../../../../components/saved_views/toolbar_control'; -import { useWaffleViewState } from '../hooks/use_waffle_view_state'; +import { useWaffleViewState, WaffleViewState } from '../hooks/use_waffle_view_state'; export const SavedViews = () => { const { viewState } = useWaffleViewState(); - return ; + const { + currentView, + views, + isFetchingViews, + isFetchingCurrentView, + isCreatingView, + isUpdatingView, + createView, + deleteViewById, + fetchViews, + updateViewById, + switchViewById, + setDefaultViewById, + } = useInventoryViews(); + + return ( + + currentView={currentView} + views={views} + isFetchingViews={isFetchingViews} + isFetchingCurrentView={isFetchingCurrentView} + isCreatingView={isCreatingView} + isUpdatingView={isUpdatingView} + onCreateView={createView} + onDeleteView={deleteViewById} + onUpdateView={updateViewById} + onLoadViews={fetchViews} + onSetDefaultView={setDefaultViewById} + onSwitchView={switchViewById} + viewState={viewState} + /> + ); }; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_view_state.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_view_state.ts index 02a2144f1282e..6e685a6cc105f 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_view_state.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_view_state.ts @@ -65,30 +65,32 @@ export const useWaffleViewState = () => { }; const onViewChange = useCallback( - (newState: WaffleViewState) => { + (newState) => { + const attributes = newState.attributes as WaffleViewState; + setWaffleOptionsState({ - sort: newState.sort, - metric: newState.metric, - groupBy: newState.groupBy, - nodeType: newState.nodeType, - view: newState.view, - customOptions: newState.customOptions, - customMetrics: newState.customMetrics, - boundsOverride: newState.boundsOverride, - autoBounds: newState.autoBounds, - accountId: newState.accountId, - region: newState.region, - legend: newState.legend, - timelineOpen: newState.timelineOpen, + sort: attributes.sort, + metric: attributes.metric, + groupBy: attributes.groupBy, + nodeType: attributes.nodeType, + view: attributes.view, + customOptions: attributes.customOptions, + customMetrics: attributes.customMetrics, + boundsOverride: attributes.boundsOverride, + autoBounds: attributes.autoBounds, + accountId: attributes.accountId, + region: attributes.region, + legend: attributes.legend, + timelineOpen: attributes.timelineOpen, }); - if (newState.time) { + if (attributes.time) { setWaffleTimeState({ - currentTime: newState.time, - isAutoReloading: newState.autoReload, + currentTime: attributes.time, + isAutoReloading: attributes.autoReload, }); } - setWaffleFiltersState(newState.filterQuery); + setWaffleFiltersState(attributes.filterQuery); }, [setWaffleOptionsState, setWaffleTimeState, setWaffleFiltersState] ); diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/index.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/index.tsx index 922f0cdfaaaa8..6c97172a30f82 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/index.tsx @@ -16,9 +16,6 @@ import { SourceLoadingPage } from '../../../components/source_loading_page'; import { useSourceContext } from '../../../containers/metrics_source'; import { useMetricsBreadcrumbs } from '../../../hooks/use_metrics_breadcrumbs'; import { LayoutView } from './components/layout_view'; -import { SavedViewProvider } from '../../../containers/saved_view/saved_view'; -import { DEFAULT_WAFFLE_VIEW_STATE } from './hooks/use_waffle_view_state'; -import { useWaffleOptionsContext } from './hooks/use_waffle_options'; import { MetricsPageTemplate } from '../page_template'; import { inventoryTitle } from '../../../translations'; import { SavedViews } from './components/saved_views'; @@ -32,7 +29,6 @@ export const SnapshotPage = () => { useTrackPageview({ app: 'infra_metrics', path: 'inventory' }); useTrackPageview({ app: 'infra_metrics', path: 'inventory', delay: 15000 }); - const { source: optionsSource } = useWaffleOptionsContext(); useMetricsBreadcrumbs([ { @@ -60,36 +56,30 @@ export const SnapshotPage = () => { return (
- , ], + }} + pageSectionProps={{ + contentProps: { + css: css` + ${fullHeightContentStyles}; + padding-bottom: 0; + `, + }, + }} > - , ], - }} - pageSectionProps={{ - contentProps: { - css: css` - ${fullHeightContentStyles}; - padding-bottom: 0; - `, - }, - }} - > - ( - <> - - - - )} - /> - - + ( + <> + + + + )} + /> +
); diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/saved_views.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/saved_views.tsx new file mode 100644 index 0000000000000..2d329f121f008 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/saved_views.tsx @@ -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 React from 'react'; +import { useMetricsExplorerViews } from '../../../../hooks/use_metrics_explorer_views'; +import { SavedViewsToolbarControls } from '../../../../components/saved_views/toolbar_control'; +import { MetricExplorerViewState } from '../hooks/use_metric_explorer_state'; + +interface Props { + viewState: MetricExplorerViewState; +} + +export const SavedViews = ({ viewState }: Props) => { + const { + currentView, + views, + isFetchingViews, + isFetchingCurrentView, + isCreatingView, + isUpdatingView, + createView, + deleteViewById, + fetchViews, + updateViewById, + switchViewById, + setDefaultViewById, + } = useMetricsExplorerViews(); + + return ( + + currentView={currentView} + views={views} + isFetchingViews={isFetchingViews} + isFetchingCurrentView={isFetchingCurrentView} + isCreatingView={isCreatingView} + isUpdatingView={isUpdatingView} + onCreateView={createView} + onDeleteView={deleteViewById} + onUpdateView={updateViewById} + onLoadViews={fetchViews} + onSetDefaultView={setDefaultViewById} + onSwitchView={switchViewById} + viewState={viewState} + /> + ); +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.ts index a9a4611094174..bc098e441eb29 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.ts @@ -8,6 +8,7 @@ import DateMath from '@kbn/datemath'; import { useCallback, useEffect } from 'react'; import { DataViewBase } from '@kbn/es-query'; +import { MetricsExplorerView } from '../../../../../common/metrics_explorer_views'; import { MetricsSourceConfigurationProperties } from '../../../../../common/metrics_sources'; import { MetricsExplorerMetric, @@ -124,19 +125,19 @@ export const useMetricsExplorerState = ( ); const onViewStateChange = useCallback( - (vs: MetricExplorerViewState) => { - if (vs.chartOptions) { - setChartOptions(vs.chartOptions); + (view: MetricsExplorerView) => { + if (view.attributes.chartOptions) { + setChartOptions(view.attributes.chartOptions as MetricsExplorerChartOptions); } - if (vs.currentTimerange) { + if (view.attributes.currentTimerange) { // if this is the "Default View" view, don't update the time range to the view's time range, // this way it will use the global Kibana time or the default time already set - if (vs.id !== '0') { - setTimeRange(vs.currentTimerange); + if (!view.attributes.isStatic) { + setTimeRange(view.attributes.currentTimerange as MetricsExplorerTimeOptions); } } - if (vs.options) { - setOptions(vs.options); + if (view.attributes.options) { + setOptions(view.attributes.options as MetricsExplorerOptions); } }, [setChartOptions, setOptions, setTimeRange] diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/index.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/index.tsx index d62d6ea1e82b1..07d5e210c46a1 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/index.tsx @@ -9,6 +9,7 @@ import { EuiErrorBoundary } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useEffect, useState } from 'react'; import { useTrackPageview } from '@kbn/observability-plugin/public'; +import { useMetricsExplorerViews } from '../../../hooks/use_metrics_explorer_views'; import { MetricsSourceConfigurationProperties } from '../../../../common/metrics_sources'; import { useMetricsBreadcrumbs } from '../../../hooks/use_metrics_breadcrumbs'; import { NoData } from '../../../components/empty_states'; @@ -16,11 +17,10 @@ import { MetricsExplorerCharts } from './components/charts'; import { MetricsExplorerToolbar } from './components/toolbar'; import { useMetricsExplorerState } from './hooks/use_metric_explorer_state'; import { useSourceContext } from '../../../containers/metrics_source'; -import { useSavedViewContext } from '../../../containers/saved_view/saved_view'; import { MetricsPageTemplate } from '../page_template'; import { metricsExplorerTitle } from '../../../translations'; -import { SavedViewsToolbarControls } from '../../../components/saved_views/toolbar_control'; import { DerivedIndexPattern } from '../../../containers/metrics_source'; +import { SavedViews } from './components/saved_views'; interface MetricsExplorerPageProps { source: MetricsSourceConfigurationProperties; derivedIndexPattern: DerivedIndexPattern; @@ -45,7 +45,7 @@ export const MetricsExplorerPage = ({ source, derivedIndexPattern }: MetricsExpl onViewStateChange, refresh, } = useMetricsExplorerState(source, derivedIndexPattern, enabled); - const { currentView, shouldLoadDefault } = useSavedViewContext(); + const { currentView } = useMetricsExplorerViews(); useTrackPageview({ app: 'infra_metrics', path: 'metrics_explorer' }); useTrackPageview({ app: 'infra_metrics', path: 'metrics_explorer', delay: 15000 }); @@ -58,11 +58,11 @@ export const MetricsExplorerPage = ({ source, derivedIndexPattern }: MetricsExpl }, [currentView, onViewStateChange]); useEffect(() => { - if (currentView != null || !shouldLoadDefault) { + if (currentView != null) { // load metrics explorer data after default view loaded, unless we're not isLoading a view setEnabled(true); } - }, [currentView, shouldLoadDefault]); + }, [currentView]); useMetricsBreadcrumbs([ { @@ -70,21 +70,19 @@ export const MetricsExplorerPage = ({ source, derivedIndexPattern }: MetricsExpl }, ]); + const viewState = { + options, + chartOptions, + currentTimerange: timeRange, + }; + return ( , - ], + rightSideItems: [], }} > { - throw new UpsertInventoryViewError(`Failed to create new inventory view : ${error}`); + throw new UpsertInventoryViewError( + `Failed to create new inventory view: ${error.body?.message ?? error.message}` + ); }); const { data } = decodeOrThrow( @@ -92,7 +94,9 @@ export class InventoryViewsClient implements IInventoryViewsClient { }) .catch((error) => { throw new UpsertInventoryViewError( - `Failed to update inventory view "${inventoryViewId}": ${error}` + `Failed to update inventory view "${inventoryViewId}": ${ + error.body?.message ?? error.message + }` ); }); diff --git a/x-pack/plugins/infra/public/services/inventory_views/types.ts b/x-pack/plugins/infra/public/services/inventory_views/types.ts index 2a690c4cc6c2e..573c144e9c441 100644 --- a/x-pack/plugins/infra/public/services/inventory_views/types.ts +++ b/x-pack/plugins/infra/public/services/inventory_views/types.ts @@ -6,7 +6,11 @@ */ import { HttpStart } from '@kbn/core/public'; -import { InventoryView, InventoryViewAttributes } from '../../../common/inventory_views'; +import { + CreateInventoryViewAttributesRequestPayload, + UpdateInventoryViewAttributesRequestPayload, +} from '../../../common/http_api/latest'; +import type { InventoryView } from '../../../common/inventory_views'; export type InventoryViewsServiceSetup = void; @@ -22,11 +26,11 @@ export interface IInventoryViewsClient { findInventoryViews(): Promise; getInventoryView(inventoryViewId: string): Promise; createInventoryView( - inventoryViewAttributes: Partial + inventoryViewAttributes: CreateInventoryViewAttributesRequestPayload ): Promise; updateInventoryView( inventoryViewId: string, - inventoryViewAttributes: Partial + inventoryViewAttributes: UpdateInventoryViewAttributesRequestPayload ): Promise; deleteInventoryView(inventoryViewId: string): Promise; } diff --git a/x-pack/plugins/infra/public/services/metrics_explorer_views/metrics_explorer_views_client.ts b/x-pack/plugins/infra/public/services/metrics_explorer_views/metrics_explorer_views_client.ts index d37d2fc81b31d..788a8789abe73 100644 --- a/x-pack/plugins/infra/public/services/metrics_explorer_views/metrics_explorer_views_client.ts +++ b/x-pack/plugins/infra/public/services/metrics_explorer_views/metrics_explorer_views_client.ts @@ -73,7 +73,7 @@ export class MetricsExplorerViewsClient implements IMetricsExplorerViewsClient { }) .catch((error) => { throw new UpsertMetricsExplorerViewError( - `Failed to create new metrics explorer view: ${error}` + `Failed to create new metrics explorer view: ${error.body?.message ?? error.message}` ); }); @@ -102,7 +102,9 @@ export class MetricsExplorerViewsClient implements IMetricsExplorerViewsClient { }) .catch((error) => { throw new UpsertMetricsExplorerViewError( - `Failed to update metrics explorer view "${metricsExplorerViewId}": ${error}` + `Failed to update metrics explorer view "${metricsExplorerViewId}": ${ + error.body?.message ?? error.message + }` ); }); diff --git a/x-pack/plugins/infra/server/lib/infra_ml/metrics_hosts_anomalies.ts b/x-pack/plugins/infra/server/lib/infra_ml/metrics_hosts_anomalies.ts index 1e307dae8e5b7..5aa53c67544f5 100644 --- a/x-pack/plugins/infra/server/lib/infra_ml/metrics_hosts_anomalies.ts +++ b/x-pack/plugins/infra/server/lib/infra_ml/metrics_hosts_anomalies.ts @@ -5,10 +5,11 @@ * 2.0. */ +import { ML_ANOMALY_THRESHOLD } from '@kbn/ml-anomaly-utils/anomaly_threshold'; import { InfraRequestHandlerContext } from '../../types'; import { TracingSpan, startTracingSpan } from '../../../common/performance_tracing'; import { fetchMlJob, MappedAnomalyHit, InfluencerFilter } from './common'; -import { getJobId, metricsHostsJobTypes, ANOMALY_THRESHOLD } from '../../../common/infra_ml'; +import { getJobId, metricsHostsJobTypes } from '../../../common/infra_ml'; import { Sort, Pagination } from '../../../common/http_api/infra_ml'; import type { MlSystem, MlAnomalyDetectors } from '../../types'; import { isMlPrivilegesError } from './errors'; @@ -74,7 +75,7 @@ export async function getMetricsHostsAnomalies({ }: { context: Required; sourceId: string; - anomalyThreshold: ANOMALY_THRESHOLD; + anomalyThreshold: ML_ANOMALY_THRESHOLD; startTime: number; endTime: number; metric: 'memory_usage' | 'network_in' | 'network_out' | undefined; @@ -172,7 +173,7 @@ const parseAnomalyResult = (anomaly: MappedAnomalyHit, jobId: string) => { async function fetchMetricsHostsAnomalies( mlSystem: MlSystem, - anomalyThreshold: ANOMALY_THRESHOLD, + anomalyThreshold: ML_ANOMALY_THRESHOLD, jobIds: string[], startTime: number, endTime: number, diff --git a/x-pack/plugins/infra/server/lib/infra_ml/metrics_k8s_anomalies.ts b/x-pack/plugins/infra/server/lib/infra_ml/metrics_k8s_anomalies.ts index 00635caee96fc..81d2300604688 100644 --- a/x-pack/plugins/infra/server/lib/infra_ml/metrics_k8s_anomalies.ts +++ b/x-pack/plugins/infra/server/lib/infra_ml/metrics_k8s_anomalies.ts @@ -5,10 +5,11 @@ * 2.0. */ +import { ML_ANOMALY_THRESHOLD } from '@kbn/ml-anomaly-utils/anomaly_threshold'; import { InfraRequestHandlerContext } from '../../types'; import { TracingSpan, startTracingSpan } from '../../../common/performance_tracing'; import { fetchMlJob, MappedAnomalyHit, InfluencerFilter } from './common'; -import { getJobId, metricsK8SJobTypes, ANOMALY_THRESHOLD } from '../../../common/infra_ml'; +import { getJobId, metricsK8SJobTypes } from '../../../common/infra_ml'; import { Sort, Pagination } from '../../../common/http_api/infra_ml'; import type { MlSystem, MlAnomalyDetectors } from '../../types'; import { isMlPrivilegesError } from './errors'; @@ -74,7 +75,7 @@ export async function getMetricK8sAnomalies({ }: { context: Required; sourceId: string; - anomalyThreshold: ANOMALY_THRESHOLD; + anomalyThreshold: ML_ANOMALY_THRESHOLD; startTime: number; endTime: number; metric: 'memory_usage' | 'network_in' | 'network_out' | undefined; @@ -168,7 +169,7 @@ const parseAnomalyResult = (anomaly: MappedAnomalyHit, jobId: string) => { async function fetchMetricK8sAnomalies( mlSystem: MlSystem, - anomalyThreshold: ANOMALY_THRESHOLD, + anomalyThreshold: ML_ANOMALY_THRESHOLD, jobIds: string[], startTime: number, endTime: number, diff --git a/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_hosts_anomalies.ts b/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_hosts_anomalies.ts index 9c0f4313c6bdb..2d1efebc51666 100644 --- a/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_hosts_anomalies.ts +++ b/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_hosts_anomalies.ts @@ -6,8 +6,8 @@ */ import * as rt from 'io-ts'; +import { ML_ANOMALY_THRESHOLD } from '@kbn/ml-anomaly-utils/anomaly_threshold'; import { TIEBREAKER_FIELD } from '../../../../common/constants'; -import { ANOMALY_THRESHOLD } from '../../../../common/infra_ml'; import { commonSearchSuccessResponseFieldsRT } from '../../../utils/elasticsearch_runtime_types'; import { createJobIdsFilters, @@ -38,7 +38,7 @@ export const createMetricsHostsAnomaliesQuery = ({ jobQuery, }: { jobIds: string[]; - anomalyThreshold: ANOMALY_THRESHOLD; + anomalyThreshold: ML_ANOMALY_THRESHOLD; startTime: number; endTime: number; sort: Sort; diff --git a/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_k8s_anomalies.ts b/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_k8s_anomalies.ts index 23592aad2e322..83375ae1e8d2b 100644 --- a/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_k8s_anomalies.ts +++ b/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_k8s_anomalies.ts @@ -6,8 +6,8 @@ */ import * as rt from 'io-ts'; +import { ML_ANOMALY_THRESHOLD } from '@kbn/ml-anomaly-utils/anomaly_threshold'; import { TIEBREAKER_FIELD } from '../../../../common/constants'; -import { ANOMALY_THRESHOLD } from '../../../../common/infra_ml'; import { commonSearchSuccessResponseFieldsRT } from '../../../utils/elasticsearch_runtime_types'; import { createJobIdsFilters, @@ -38,7 +38,7 @@ export const createMetricsK8sAnomaliesQuery = ({ jobQuery, }: { jobIds: string[]; - anomalyThreshold: ANOMALY_THRESHOLD; + anomalyThreshold: ML_ANOMALY_THRESHOLD; startTime: number; endTime: number; sort: Sort; diff --git a/x-pack/plugins/infra/server/routes/inventory_views/create_inventory_view.ts b/x-pack/plugins/infra/server/routes/inventory_views/create_inventory_view.ts index 90bb47d8a2d76..d2df0a0f96f65 100644 --- a/x-pack/plugins/infra/server/routes/inventory_views/create_inventory_view.ts +++ b/x-pack/plugins/infra/server/routes/inventory_views/create_inventory_view.ts @@ -9,6 +9,7 @@ import { isBoom } from '@hapi/boom'; import { createValidationFunction } from '../../../common/runtime_types'; import { createInventoryViewRequestPayloadRT, + inventoryViewRequestQueryRT, inventoryViewResponsePayloadRT, INVENTORY_VIEW_URL, } from '../../../common/http_api/latest'; @@ -24,15 +25,16 @@ export const initCreateInventoryViewRoute = ({ path: INVENTORY_VIEW_URL, validate: { body: createValidationFunction(createInventoryViewRequestPayloadRT), + query: createValidationFunction(inventoryViewRequestQueryRT), }, }, async (_requestContext, request, response) => { - const { body } = request; + const { body, query } = request; const [, , { inventoryViews }] = await getStartServices(); const inventoryViewsClient = inventoryViews.getScopedClient(request); try { - const inventoryView = await inventoryViewsClient.create(body.attributes); + const inventoryView = await inventoryViewsClient.update(null, body.attributes, query); return response.custom({ statusCode: 201, diff --git a/x-pack/plugins/infra/server/routes/metrics_explorer_views/create_metrics_explorer_view.ts b/x-pack/plugins/infra/server/routes/metrics_explorer_views/create_metrics_explorer_view.ts index 948dd757e7e01..d02ed1208eb11 100644 --- a/x-pack/plugins/infra/server/routes/metrics_explorer_views/create_metrics_explorer_view.ts +++ b/x-pack/plugins/infra/server/routes/metrics_explorer_views/create_metrics_explorer_view.ts @@ -9,6 +9,7 @@ import { isBoom } from '@hapi/boom'; import { createValidationFunction } from '../../../common/runtime_types'; import { createMetricsExplorerViewRequestPayloadRT, + metricsExplorerViewRequestQueryRT, metricsExplorerViewResponsePayloadRT, METRICS_EXPLORER_VIEW_URL, } from '../../../common/http_api/latest'; @@ -24,15 +25,20 @@ export const initCreateMetricsExplorerViewRoute = ({ path: METRICS_EXPLORER_VIEW_URL, validate: { body: createValidationFunction(createMetricsExplorerViewRequestPayloadRT), + query: createValidationFunction(metricsExplorerViewRequestQueryRT), }, }, async (_requestContext, request, response) => { - const { body } = request; + const { body, query } = request; const [, , { metricsExplorerViews }] = await getStartServices(); const metricsExplorerViewsClient = metricsExplorerViews.getScopedClient(request); try { - const metricsExplorerView = await metricsExplorerViewsClient.create(body.attributes); + const metricsExplorerView = await metricsExplorerViewsClient.update( + null, + body.attributes, + query + ); return response.custom({ statusCode: 201, diff --git a/x-pack/plugins/infra/server/services/inventory_views/inventory_views_client.mock.ts b/x-pack/plugins/infra/server/services/inventory_views/inventory_views_client.mock.ts index 9d832f8502104..5b21a4e43d267 100644 --- a/x-pack/plugins/infra/server/services/inventory_views/inventory_views_client.mock.ts +++ b/x-pack/plugins/infra/server/services/inventory_views/inventory_views_client.mock.ts @@ -11,6 +11,5 @@ export const createInventoryViewsClientMock = (): jest.Mocked { const mockFindInventoryList = (savedObjectsClient: jest.Mocked) => { @@ -115,45 +112,6 @@ describe('InventoryViewsClient class', () => { expect(inventoryView).toEqual(inventoryViewMock); }); - describe('.create', () => { - it('generate a new inventory view', async () => { - const { inventoryViewsClient, savedObjectsClient } = createInventoryViewsClient(); - - const inventoryViewMock = createInventoryViewMock('new_id', { - name: 'New view', - isStatic: false, - } as InventoryViewAttributes); - - mockFindInventoryList(savedObjectsClient); - - savedObjectsClient.create.mockResolvedValue({ - ...inventoryViewMock, - type: inventoryViewSavedObjectName, - references: [], - }); - - const inventoryView = await inventoryViewsClient.create({ - name: 'New view', - } as CreateInventoryViewAttributesRequestPayload); - - expect(savedObjectsClient.create).toHaveBeenCalled(); - expect(inventoryView).toEqual(inventoryViewMock); - }); - - it('throws an error when a conflicting name is given', async () => { - const { inventoryViewsClient, savedObjectsClient } = createInventoryViewsClient(); - - mockFindInventoryList(savedObjectsClient); - - await expect( - async () => - await inventoryViewsClient.create({ - name: 'Custom', - } as CreateInventoryViewAttributesRequestPayload) - ).rejects.toThrow('A view with that name already exists.'); - }); - }); - describe('.update', () => { it('update an existing inventory view by id', async () => { const { inventoryViewsClient, infraSources, savedObjectsClient } = @@ -171,7 +129,7 @@ describe('InventoryViewsClient class', () => { infraSources.getSourceConfiguration.mockResolvedValue(basicTestSourceConfiguration); - savedObjectsClient.update.mockResolvedValue({ + savedObjectsClient.create.mockResolvedValue({ ...inventoryViewMock, type: inventoryViewSavedObjectName, references: [], @@ -185,7 +143,7 @@ describe('InventoryViewsClient class', () => { {} ); - expect(savedObjectsClient.update).toHaveBeenCalled(); + expect(savedObjectsClient.create).toHaveBeenCalled(); expect(inventoryView).toEqual(inventoryViewMock); }); diff --git a/x-pack/plugins/infra/server/services/inventory_views/inventory_views_client.ts b/x-pack/plugins/infra/server/services/inventory_views/inventory_views_client.ts index c32da344354b6..5efce009da410 100644 --- a/x-pack/plugins/infra/server/services/inventory_views/inventory_views_client.ts +++ b/x-pack/plugins/infra/server/services/inventory_views/inventory_views_client.ts @@ -5,11 +5,12 @@ * 2.0. */ -import type { +import { Logger, SavedObject, SavedObjectsClientContract, SavedObjectsUpdateResponse, + SavedObjectsUtils, } from '@kbn/core/server'; import Boom from '@hapi/boom'; import { @@ -50,6 +51,7 @@ export class InventoryViewsClient implements IInventoryViewsClient { const defaultView = InventoryViewsClient.createStaticView( sourceConfiguration.configuration.inventoryDefaultView ); + const views = inventoryViewSavedObject.saved_objects.map((savedObject) => this.mapSavedObjectToInventoryView( savedObject, @@ -95,37 +97,26 @@ export class InventoryViewsClient implements IInventoryViewsClient { ); } - public async create( - attributes: CreateInventoryViewAttributesRequestPayload - ): Promise { - this.logger.debug(`Trying to create inventory view ...`); - - // Validate there is not a view with the same name - await this.assertNameConflict(attributes.name); - - const inventoryViewSavedObject = await this.savedObjectsClient.create( - inventoryViewSavedObjectName, - attributes - ); - - return this.mapSavedObjectToInventoryView(inventoryViewSavedObject); - } - public async update( - inventoryViewId: string, + inventoryViewId: string | null, attributes: CreateInventoryViewAttributesRequestPayload, query: InventoryViewRequestQuery ): Promise { this.logger.debug(`Trying to update inventory view with id "${inventoryViewId}"...`); + const viewId = inventoryViewId ?? SavedObjectsUtils.generateId(); + // Validate there is not a view with the same name - await this.assertNameConflict(attributes.name, [inventoryViewId]); + await this.assertNameConflict(attributes.name, [viewId]); const sourceId = query.sourceId ?? InventoryViewsClient.DEFAULT_SOURCE_ID; const [sourceConfiguration, inventoryViewSavedObject] = await Promise.all([ this.infraSources.getSourceConfiguration(this.savedObjectsClient, sourceId), - this.savedObjectsClient.update(inventoryViewSavedObjectName, inventoryViewId, attributes), + this.savedObjectsClient.create(inventoryViewSavedObjectName, attributes, { + id: viewId, + overwrite: true, + }), ]); return this.mapSavedObjectToInventoryView( diff --git a/x-pack/plugins/infra/server/services/inventory_views/types.ts b/x-pack/plugins/infra/server/services/inventory_views/types.ts index 3e023b77af6c2..2537203d1754c 100644 --- a/x-pack/plugins/infra/server/services/inventory_views/types.ts +++ b/x-pack/plugins/infra/server/services/inventory_views/types.ts @@ -11,7 +11,6 @@ import type { SavedObjectsServiceStart, } from '@kbn/core/server'; import type { - CreateInventoryViewAttributesRequestPayload, InventoryViewRequestQuery, UpdateInventoryViewAttributesRequestPayload, } from '../../../common/http_api/latest'; @@ -34,11 +33,8 @@ export interface IInventoryViewsClient { delete(inventoryViewId: string): Promise<{}>; find(query: InventoryViewRequestQuery): Promise; get(inventoryViewId: string, query: InventoryViewRequestQuery): Promise; - create( - inventoryViewAttributes: CreateInventoryViewAttributesRequestPayload - ): Promise; update( - inventoryViewId: string, + inventoryViewId: string | null, inventoryViewAttributes: UpdateInventoryViewAttributesRequestPayload, query: InventoryViewRequestQuery ): Promise; diff --git a/x-pack/plugins/infra/server/services/metrics_explorer_views/metrics_explorer_views_client.mock.ts b/x-pack/plugins/infra/server/services/metrics_explorer_views/metrics_explorer_views_client.mock.ts index 82a8cba3f6427..a19b8847adee1 100644 --- a/x-pack/plugins/infra/server/services/metrics_explorer_views/metrics_explorer_views_client.mock.ts +++ b/x-pack/plugins/infra/server/services/metrics_explorer_views/metrics_explorer_views_client.mock.ts @@ -12,6 +12,5 @@ export const createMetricsExplorerViewsClientMock = delete: jest.fn(), find: jest.fn(), get: jest.fn(), - create: jest.fn(), update: jest.fn(), }); diff --git a/x-pack/plugins/infra/server/services/metrics_explorer_views/metrics_explorer_views_client.test.ts b/x-pack/plugins/infra/server/services/metrics_explorer_views/metrics_explorer_views_client.test.ts index c903e9af360f8..791b7366f086c 100644 --- a/x-pack/plugins/infra/server/services/metrics_explorer_views/metrics_explorer_views_client.test.ts +++ b/x-pack/plugins/infra/server/services/metrics_explorer_views/metrics_explorer_views_client.test.ts @@ -15,10 +15,7 @@ import { createInfraSourcesMock } from '../../lib/sources/mocks'; import { metricsExplorerViewSavedObjectName } from '../../saved_objects/metrics_explorer_view'; import { MetricsExplorerViewsClient } from './metrics_explorer_views_client'; import { createMetricsExplorerViewMock } from '../../../common/metrics_explorer_views/metrics_explorer_view.mock'; -import { - CreateMetricsExplorerViewAttributesRequestPayload, - UpdateMetricsExplorerViewAttributesRequestPayload, -} from '../../../common/http_api/latest'; +import { UpdateMetricsExplorerViewAttributesRequestPayload } from '../../../common/http_api/latest'; describe('MetricsExplorerViewsClient class', () => { const mockFindMetricsExplorerList = ( @@ -118,45 +115,6 @@ describe('MetricsExplorerViewsClient class', () => { expect(metricsExplorerView).toEqual(metricsExplorerViewMock); }); - describe('.create', () => { - it('generate a new metrics explorer view', async () => { - const { metricsExplorerViewsClient, savedObjectsClient } = createMetricsExplorerViewsClient(); - - const metricsExplorerViewMock = createMetricsExplorerViewMock('new_id', { - name: 'New view', - isStatic: false, - } as MetricsExplorerViewAttributes); - - mockFindMetricsExplorerList(savedObjectsClient); - - savedObjectsClient.create.mockResolvedValue({ - ...metricsExplorerViewMock, - type: metricsExplorerViewSavedObjectName, - references: [], - }); - - const metricsExplorerView = await metricsExplorerViewsClient.create({ - name: 'New view', - } as CreateMetricsExplorerViewAttributesRequestPayload); - - expect(savedObjectsClient.create).toHaveBeenCalled(); - expect(metricsExplorerView).toEqual(metricsExplorerViewMock); - }); - - it('throws an error when a conflicting name is given', async () => { - const { metricsExplorerViewsClient, savedObjectsClient } = createMetricsExplorerViewsClient(); - - mockFindMetricsExplorerList(savedObjectsClient); - - await expect( - async () => - await metricsExplorerViewsClient.create({ - name: 'Custom', - } as CreateMetricsExplorerViewAttributesRequestPayload) - ).rejects.toThrow('A view with that name already exists.'); - }); - }); - describe('.update', () => { it('update an existing metrics explorer view by id', async () => { const { metricsExplorerViewsClient, infraSources, savedObjectsClient } = @@ -174,7 +132,7 @@ describe('MetricsExplorerViewsClient class', () => { infraSources.getSourceConfiguration.mockResolvedValue(basicTestSourceConfiguration); - savedObjectsClient.update.mockResolvedValue({ + savedObjectsClient.create.mockResolvedValue({ ...metricsExplorerViewMock, type: metricsExplorerViewSavedObjectName, references: [], @@ -188,7 +146,7 @@ describe('MetricsExplorerViewsClient class', () => { {} ); - expect(savedObjectsClient.update).toHaveBeenCalled(); + expect(savedObjectsClient.create).toHaveBeenCalled(); expect(metricsExplorerView).toEqual(metricsExplorerViewMock); }); diff --git a/x-pack/plugins/infra/server/services/metrics_explorer_views/metrics_explorer_views_client.ts b/x-pack/plugins/infra/server/services/metrics_explorer_views/metrics_explorer_views_client.ts index 1ba34456d88a8..e2dd15940bb19 100644 --- a/x-pack/plugins/infra/server/services/metrics_explorer_views/metrics_explorer_views_client.ts +++ b/x-pack/plugins/infra/server/services/metrics_explorer_views/metrics_explorer_views_client.ts @@ -5,11 +5,12 @@ * 2.0. */ -import type { +import { Logger, SavedObject, SavedObjectsClientContract, SavedObjectsUpdateResponse, + SavedObjectsUtils, } from '@kbn/core/server'; import Boom from '@hapi/boom'; import { @@ -98,24 +99,8 @@ export class MetricsExplorerViewsClient implements IMetricsExplorerViewsClient { ); } - public async create( - attributes: CreateMetricsExplorerViewAttributesRequestPayload - ): Promise { - this.logger.debug(`Trying to create metrics explorer view ...`); - - // Validate there is not a view with the same name - await this.assertNameConflict(attributes.name); - - const metricsExplorerViewSavedObject = await this.savedObjectsClient.create( - metricsExplorerViewSavedObjectName, - attributes - ); - - return this.mapSavedObjectToMetricsExplorerView(metricsExplorerViewSavedObject); - } - public async update( - metricsExplorerViewId: string, + metricsExplorerViewId: string | null, attributes: CreateMetricsExplorerViewAttributesRequestPayload, query: MetricsExplorerViewRequestQuery ): Promise { @@ -123,18 +108,19 @@ export class MetricsExplorerViewsClient implements IMetricsExplorerViewsClient { `Trying to update metrics explorer view with id "${metricsExplorerViewId}"...` ); + const viewId = metricsExplorerViewId ?? SavedObjectsUtils.generateId(); + // Validate there is not a view with the same name - await this.assertNameConflict(attributes.name, [metricsExplorerViewId]); + await this.assertNameConflict(attributes.name, [viewId]); const sourceId = query.sourceId ?? MetricsExplorerViewsClient.DEFAULT_SOURCE_ID; const [sourceConfiguration, metricsExplorerViewSavedObject] = await Promise.all([ this.infraSources.getSourceConfiguration(this.savedObjectsClient, sourceId), - this.savedObjectsClient.update( - metricsExplorerViewSavedObjectName, - metricsExplorerViewId, - attributes - ), + this.savedObjectsClient.create(metricsExplorerViewSavedObjectName, attributes, { + id: viewId, + overwrite: true, + }), ]); return this.mapSavedObjectToMetricsExplorerView( diff --git a/x-pack/plugins/infra/server/services/metrics_explorer_views/types.ts b/x-pack/plugins/infra/server/services/metrics_explorer_views/types.ts index 0e64aaa83d27e..851cdf3ad77f0 100644 --- a/x-pack/plugins/infra/server/services/metrics_explorer_views/types.ts +++ b/x-pack/plugins/infra/server/services/metrics_explorer_views/types.ts @@ -11,7 +11,6 @@ import type { SavedObjectsServiceStart, } from '@kbn/core/server'; import type { - CreateMetricsExplorerViewAttributesRequestPayload, MetricsExplorerViewRequestQuery, UpdateMetricsExplorerViewAttributesRequestPayload, } from '../../../common/http_api/latest'; @@ -37,11 +36,8 @@ export interface IMetricsExplorerViewsClient { metricsExplorerViewId: string, query: MetricsExplorerViewRequestQuery ): Promise; - create( - metricsExplorerViewAttributes: CreateMetricsExplorerViewAttributesRequestPayload - ): Promise; update( - metricsExplorerViewId: string, + metricsExplorerViewId: string | null, metricsExplorerViewAttributes: UpdateMetricsExplorerViewAttributesRequestPayload, query: MetricsExplorerViewRequestQuery ): Promise; diff --git a/x-pack/plugins/infra/tsconfig.json b/x-pack/plugins/infra/tsconfig.json index 76cbce409ce2f..2f86464b357fb 100644 --- a/x-pack/plugins/infra/tsconfig.json +++ b/x-pack/plugins/infra/tsconfig.json @@ -63,6 +63,7 @@ "@kbn/observability-alert-details", "@kbn/observability-shared-plugin", "@kbn/ui-theme", + "@kbn/ml-anomaly-utils", ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/ml/common/constants/anomalies.ts b/x-pack/plugins/ml/common/constants/anomalies.ts deleted file mode 100644 index 3c031f28f4d3f..0000000000000 --- a/x-pack/plugins/ml/common/constants/anomalies.ts +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/** - * Labels displayed in the ML UI to indicate the severity of the anomaly according - * to the normalized anomaly score. - */ -export enum ANOMALY_SEVERITY { - /** - * Anomalies are displayed as critical severity when the score is greater than or equal to 75. - */ - CRITICAL = 'critical', - - /** - * Anomalies are displayed as major severity when the score is greater than or equal to 50 and less than 75. - */ - MAJOR = 'major', - - /** - * Anomalies are displayed as minor severity when the score is greater than or equal to 25 and less than 50. - */ - MINOR = 'minor', - - /** - * Anomalies are displayed as warning severity when the score is greater than or equal to 3 and less than 25. - * Note in some parts of the UI, warning severity is used when the score is greater than or equal to 0. - */ - WARNING = 'warning', - - /** - * Anomalies are displayed as low severity in some parts of the ML UI when the score is greater than or equal to 0 and less than 3. - */ - LOW = 'low', - - /** - * Anomalies are displayed as unknown severity if the anomaly score is not known. - */ - UNKNOWN = 'unknown', -} - -/** - * Anomaly score numeric thresholds to indicate the severity of the anomaly. - */ -export enum ANOMALY_THRESHOLD { - /** - * Threshold at which anomalies are labelled in the UI as critical. - */ - CRITICAL = 75, - - /** - * Threshold at which anomalies are labelled in the UI as major. - */ - MAJOR = 50, - - /** - * Threshold at which anomalies are labelled in the UI as minor. - */ - MINOR = 25, - - /** - * Threshold at which anomalies are labelled in the UI as warning. - */ - WARNING = 3, - - /** - * Threshold at which anomalies are labelled in the UI as low. - */ - LOW = 0, -} - -/** - * RGB hex codes used to indicate the severity of an anomaly according to its anomaly score. - */ -export const SEVERITY_COLORS = { - /** - * Color used in the UI to indicate a critical anomaly, with a score greater than or equal to 75. - */ - CRITICAL: '#fe5050', - - /** - * Color used in the UI to indicate a major anomaly, with a score greater than or equal to 50 and less than 75 . - */ - MAJOR: '#fba740', - - /** - * Color used in the UI to indicate a minor anomaly, with a score greater than or equal to 25 and less than 50. - */ - MINOR: '#fdec25', - - /** - * Color used in the UI to indicate a warning anomaly, with a score greater than or equal to 3 and less than 25. - * Note in some parts of the UI, warning severity is used when the score is greater than or equal to 0. - */ - WARNING: '#8bc8fb', - - /** - * Color used in some parts of the UI to indicate a low severity anomaly, with a score greater than or equal to 0 and less than 3. - */ - LOW: '#d2e9f7', - - /** - * Color used in the UI to indicate an anomaly for which the score is unknown. - */ - BLANK: '#ffffff', -}; - -export const SEVERITY_COLOR_RAMP = [ - { - stop: ANOMALY_THRESHOLD.LOW, - color: SEVERITY_COLORS.WARNING, - }, - { - stop: ANOMALY_THRESHOLD.MINOR, - color: SEVERITY_COLORS.MINOR, - }, - { - stop: ANOMALY_THRESHOLD.MAJOR, - color: SEVERITY_COLORS.MAJOR, - }, - { - stop: ANOMALY_THRESHOLD.CRITICAL, - color: SEVERITY_COLORS.CRITICAL, - }, -]; - -export const ANOMALY_RESULT_TYPE = { - BUCKET: 'bucket', - RECORD: 'record', - INFLUENCER: 'influencer', -} as const; - -export const PARTITION_FIELDS = ['partition_field', 'over_field', 'by_field'] as const; -export const JOB_ID = 'job_id'; -export const PARTITION_FIELD_VALUE = 'partition_field_value'; diff --git a/x-pack/plugins/ml/common/index.ts b/x-pack/plugins/ml/common/index.ts index b540c4d3751f1..90e1501d6da81 100644 --- a/x-pack/plugins/ml/common/index.ts +++ b/x-pack/plugins/ml/common/index.ts @@ -7,13 +7,6 @@ export { ES_CLIENT_TOTAL_HITS_RELATION } from './types/es_client'; export type { ChartData } from './types/field_histograms'; -export { - ANOMALY_SEVERITY, - ANOMALY_THRESHOLD, - SEVERITY_COLOR_RAMP, - SEVERITY_COLORS, -} from './constants/anomalies'; -export { getSeverityColor, getSeverityType } from './util/anomaly_utils'; export { composeValidators, patternValidator } from './util/validators'; export { isRuntimeMappings, isRuntimeField } from './util/runtime_field_utils'; export type { RuntimeMappings } from './types/fields'; diff --git a/x-pack/plugins/ml/common/types/alerts.ts b/x-pack/plugins/ml/common/types/alerts.ts index 0a6e7813db347..267096e105ef6 100644 --- a/x-pack/plugins/ml/common/types/alerts.ts +++ b/x-pack/plugins/ml/common/types/alerts.ts @@ -6,8 +6,7 @@ */ import type { RuleTypeParams, Rule } from '@kbn/alerting-plugin/common'; -import { AnomalyResultType } from './anomalies'; -import { ANOMALY_RESULT_TYPE } from '../constants/anomalies'; +import { type MlAnomalyResultType, ML_ANOMALY_RESULT_TYPE } from '@kbn/ml-anomaly-utils'; export type PreviewResultsKeys = 'record_results' | 'bucket_results' | 'influencer_results'; export type TopHitsResultsKeys = 'top_record_hits' | 'top_bucket_hits' | 'top_influencer_hits'; @@ -34,7 +33,7 @@ export interface PreviewResponse { } interface BaseAnomalyAlertDoc { - result_type: AnomalyResultType; + result_type: MlAnomalyResultType; job_id: string; /** * Rounded score @@ -46,7 +45,7 @@ interface BaseAnomalyAlertDoc { } export interface RecordAnomalyAlertDoc extends BaseAnomalyAlertDoc { - result_type: typeof ANOMALY_RESULT_TYPE.RECORD; + result_type: typeof ML_ANOMALY_RESULT_TYPE.RECORD; function: string; field_name?: string; by_field_name?: string; @@ -60,7 +59,7 @@ export interface RecordAnomalyAlertDoc extends BaseAnomalyAlertDoc { } export interface BucketAnomalyAlertDoc extends BaseAnomalyAlertDoc { - result_type: typeof ANOMALY_RESULT_TYPE.BUCKET; + result_type: typeof ML_ANOMALY_RESULT_TYPE.BUCKET; start: number; end: number; timestamp_epoch: number; @@ -68,7 +67,7 @@ export interface BucketAnomalyAlertDoc extends BaseAnomalyAlertDoc { } export interface InfluencerAnomalyAlertDoc extends BaseAnomalyAlertDoc { - result_type: typeof ANOMALY_RESULT_TYPE.INFLUENCER; + result_type: typeof ML_ANOMALY_RESULT_TYPE.INFLUENCER; influencer_field_name: string; influencer_field_value: string | number; influencer_score: number; @@ -77,15 +76,15 @@ export interface InfluencerAnomalyAlertDoc extends BaseAnomalyAlertDoc { export type AlertHitDoc = RecordAnomalyAlertDoc | BucketAnomalyAlertDoc | InfluencerAnomalyAlertDoc; export function isRecordAnomalyAlertDoc(arg: any): arg is RecordAnomalyAlertDoc { - return arg.hasOwnProperty('result_type') && arg.result_type === ANOMALY_RESULT_TYPE.RECORD; + return arg.hasOwnProperty('result_type') && arg.result_type === ML_ANOMALY_RESULT_TYPE.RECORD; } export function isBucketAnomalyAlertDoc(arg: any): arg is BucketAnomalyAlertDoc { - return arg.hasOwnProperty('result_type') && arg.result_type === ANOMALY_RESULT_TYPE.BUCKET; + return arg.hasOwnProperty('result_type') && arg.result_type === ML_ANOMALY_RESULT_TYPE.BUCKET; } export function isInfluencerAnomalyAlertDoc(arg: any): arg is InfluencerAnomalyAlertDoc { - return arg.hasOwnProperty('result_type') && arg.result_type === ANOMALY_RESULT_TYPE.INFLUENCER; + return arg.hasOwnProperty('result_type') && arg.result_type === ML_ANOMALY_RESULT_TYPE.INFLUENCER; } export type MlAnomalyDetectionAlertParams = { @@ -94,7 +93,7 @@ export type MlAnomalyDetectionAlertParams = { groupIds?: string[]; }; severity: number; - resultType: AnomalyResultType; + resultType: MlAnomalyResultType; includeInterim: boolean; lookbackInterval: string | null | undefined; topNBuckets: number | null | undefined; diff --git a/x-pack/plugins/ml/common/types/annotations.ts b/x-pack/plugins/ml/common/types/annotations.ts index 50d7c2d3fcd2b..6962371eddbf7 100644 --- a/x-pack/plugins/ml/common/types/annotations.ts +++ b/x-pack/plugins/ml/common/types/annotations.ts @@ -59,17 +59,17 @@ // ] // } -import { PartitionFieldsType } from './anomalies'; +import { MlPartitionFieldsType } from '@kbn/ml-anomaly-utils'; import { ANNOTATION_TYPE } from '../constants/annotations'; export type AnnotationFieldName = 'partition_field_name' | 'over_field_name' | 'by_field_name'; export type AnnotationFieldValue = 'partition_field_value' | 'over_field_value' | 'by_field_value'; -export function getAnnotationFieldName(fieldType: PartitionFieldsType): AnnotationFieldName { +export function getAnnotationFieldName(fieldType: MlPartitionFieldsType): AnnotationFieldName { return `${fieldType}_name` as AnnotationFieldName; } -export function getAnnotationFieldValue(fieldType: PartitionFieldsType): AnnotationFieldValue { +export function getAnnotationFieldValue(fieldType: MlPartitionFieldsType): AnnotationFieldValue { return `${fieldType}_value` as AnnotationFieldValue; } diff --git a/x-pack/plugins/ml/common/types/custom_urls.ts b/x-pack/plugins/ml/common/types/custom_urls.ts deleted file mode 100644 index 340d3f8b07d14..0000000000000 --- a/x-pack/plugins/ml/common/types/custom_urls.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { isPopulatedObject } from '@kbn/ml-is-populated-object'; -import { AnomalyRecordDoc } from './anomalies'; - -/** - * Base Interface for basic custom URL. - */ -export interface BaseUrlConfig { - url_name: string; - url_value: string; -} - -export interface KibanaUrlConfig extends BaseUrlConfig { - time_range?: string; -} - -export interface KibanaUrlConfigWithTimeRange extends BaseUrlConfig { - time_range: string; -} - -export type UrlConfig = BaseUrlConfig | KibanaUrlConfig; - -export interface CustomUrlAnomalyRecordDoc extends AnomalyRecordDoc { - earliest: string; - latest: string; -} - -export function isKibanaUrlConfigWithTimeRange(arg: unknown): arg is KibanaUrlConfigWithTimeRange { - return ( - isPopulatedObject(arg, ['url_name', 'url_value', 'time_range']) && - typeof arg.time_range === 'string' - ); -} diff --git a/x-pack/plugins/ml/common/types/data_frame_analytics.ts b/x-pack/plugins/ml/common/types/data_frame_analytics.ts index cba66124bab4b..499c6c4565054 100644 --- a/x-pack/plugins/ml/common/types/data_frame_analytics.ts +++ b/x-pack/plugins/ml/common/types/data_frame_analytics.ts @@ -10,8 +10,8 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import { EsErrorBody } from '@kbn/ml-error-utils'; +import type { MlUrlConfig } from '@kbn/ml-anomaly-utils'; import { ANALYSIS_CONFIG_TYPE } from '../constants/data_frame_analytics'; -import type { UrlConfig } from './custom_urls'; export interface DeleteDataFrameAnalyticsWithIndexStatus { success: boolean; @@ -59,7 +59,7 @@ export interface ClassificationAnalysis { export type AnalysisConfig = estypes.MlDataframeAnalysisContainer; export interface DataFrameAnalyticsMeta { - custom_urls?: UrlConfig[]; + custom_urls?: MlUrlConfig[]; [key: string]: any; } export interface DataFrameAnalyticsConfig diff --git a/x-pack/plugins/ml/common/types/detector_rules.ts b/x-pack/plugins/ml/common/types/detector_rules.ts index 787cb2350c4fc..f49ff21092e66 100644 --- a/x-pack/plugins/ml/common/types/detector_rules.ts +++ b/x-pack/plugins/ml/common/types/detector_rules.ts @@ -5,23 +5,28 @@ * 2.0. */ -import { ACTION, FILTER_TYPE, APPLIES_TO, OPERATOR } from '../constants/detector_rule'; +import { + ML_DETECTOR_RULE_ACTION, + ML_DETECTOR_RULE_FILTER_TYPE, + ML_DETECTOR_RULE_APPLIES_TO, + ML_DETECTOR_RULE_OPERATOR, +} from '@kbn/ml-anomaly-utils'; export interface DetectorRuleScope { [id: string]: { filter_id: string; - filter_type: FILTER_TYPE; + filter_type: ML_DETECTOR_RULE_FILTER_TYPE; }; } export interface DetectorRuleCondition { - applies_to: APPLIES_TO; - operator: OPERATOR; + applies_to: ML_DETECTOR_RULE_APPLIES_TO; + operator: ML_DETECTOR_RULE_OPERATOR; value: number; } export interface DetectorRule { - actions: ACTION[]; + actions: ML_DETECTOR_RULE_ACTION[]; scope?: DetectorRuleScope; conditions?: DetectorRuleCondition[]; } diff --git a/x-pack/plugins/ml/common/types/results.ts b/x-pack/plugins/ml/common/types/results.ts index 3fda97b5740b1..388c27c17b5dd 100644 --- a/x-pack/plugins/ml/common/types/results.ts +++ b/x-pack/plugins/ml/common/types/results.ts @@ -8,10 +8,9 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { LineAnnotationDatum, RectAnnotationDatum } from '@elastic/charts'; import type { ErrorType } from '@kbn/ml-error-utils'; -import type { EntityField } from '../util/anomaly_utils'; +import type { MlEntityField, MlRecordForInfluencer } from '@kbn/ml-anomaly-utils'; import type { Datafeed, JobId, ModelSnapshot } from './anomaly_detection_jobs'; import { ES_AGGREGATION, ML_JOB_AGGREGATION } from '../constants/aggregation_types'; -import type { RecordForInfluencer } from './anomalies'; export interface GetStoppedPartitionResult { jobs: string[] | Record; @@ -85,7 +84,7 @@ export interface SeriesConfigWithMetadata extends SeriesConfig { bucketSpanSeconds: number; detectorLabel?: string; fieldName: string; - entityFields: EntityField[]; + entityFields: MlEntityField[]; infoTooltip?: InfoTooltip; loading?: boolean; chartData?: ChartPoint[] | null; @@ -112,10 +111,10 @@ export interface InfoTooltip { jobId: JobId; aggregationInterval?: string; chartFunction: string; - entityFields: EntityField[]; + entityFields: MlEntityField[]; } -export interface ChartRecord extends RecordForInfluencer { +export interface ChartRecord extends MlRecordForInfluencer { function: string; } diff --git a/x-pack/plugins/ml/common/types/storage.ts b/x-pack/plugins/ml/common/types/storage.ts index ddc32cf7d24db..cb80b17bda583 100644 --- a/x-pack/plugins/ml/common/types/storage.ts +++ b/x-pack/plugins/ml/common/types/storage.ts @@ -5,9 +5,8 @@ * 2.0. */ -import { type FrozenTierPreference } from '@kbn/ml-date-picker'; - -import { EntityFieldType } from './anomalies'; +import type { MlEntityFieldType } from '@kbn/ml-anomaly-utils'; +import type { FrozenTierPreference } from '@kbn/ml-date-picker'; export const ML_ENTITY_FIELDS_CONFIG = 'ml.singleMetricViewer.partitionFields' as const; export const ML_APPLY_TIME_RANGE_CONFIG = 'ml.jobSelectorFlyout.applyTimeRange'; @@ -38,7 +37,7 @@ export type PartitionFieldConfig = | undefined; export type PartitionFieldsConfig = - | Partial> + | Partial> | undefined; export type ApplyTimeRangeConfig = boolean | undefined; diff --git a/x-pack/plugins/ml/common/util/job_utils.ts b/x-pack/plugins/ml/common/util/job_utils.ts index dd68ef6d59d83..d465a844b3ec7 100644 --- a/x-pack/plugins/ml/common/util/job_utils.ts +++ b/x-pack/plugins/ml/common/util/job_utils.ts @@ -17,6 +17,7 @@ import type { SerializableRecord } from '@kbn/utility-types'; import { FilterStateStore } from '@kbn/es-query'; import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { isDefined } from '@kbn/ml-is-defined'; +import type { MlEntityField } from '@kbn/ml-anomaly-utils'; import { ALLOWED_DATA_UNITS, JOB_ID_MAX_LENGTH } from '../constants/validation'; import { parseInterval } from './parse_interval'; import { maxLengthValidator } from './validators'; @@ -28,7 +29,6 @@ import type { Job, JobId, } from '../types/anomaly_detection_jobs'; -import type { EntityField } from './anomaly_utils'; import type { MlServerLimits } from '../types/ml_server_info'; import type { JobValidationMessage, JobValidationMessageId } from '../constants/messages'; import { ES_AGGREGATION, ML_JOB_AGGREGATION } from '../constants/aggregation_types'; @@ -283,7 +283,7 @@ export function getPartitioningFieldNames(job: CombinedJob, detectorIndex: numbe export function isModelPlotEnabled( job: Job, detectorIndex: number, - entityFields?: EntityField[] + entityFields?: MlEntityField[] ): boolean { // Check if model_plot_config is enabled. let isEnabled = job.model_plot_config?.enabled ?? false; diff --git a/x-pack/plugins/ml/public/alerting/ml_anomaly_alert_trigger.tsx b/x-pack/plugins/ml/public/alerting/ml_anomaly_alert_trigger.tsx index 980abb23e659d..f9fdfb17b319c 100644 --- a/x-pack/plugins/ml/public/alerting/ml_anomaly_alert_trigger.tsx +++ b/x-pack/plugins/ml/public/alerting/ml_anomaly_alert_trigger.tsx @@ -11,6 +11,7 @@ import useMount from 'react-use/lib/useMount'; import { i18n } from '@kbn/i18n'; import { RuleTypeParamsExpressionProps } from '@kbn/triggers-actions-ui-plugin/public'; import { isDefined } from '@kbn/ml-is-defined'; +import { ML_ANOMALY_RESULT_TYPE, ML_ANOMALY_THRESHOLD } from '@kbn/ml-anomaly-utils'; import { JobSelectorControl } from './job_selector'; import { useMlKibana } from '../application/contexts/kibana'; import { jobsApiProvider } from '../application/services/ml_api_service/jobs'; @@ -19,12 +20,10 @@ import { SeverityControl } from '../application/components/severity_control'; import { ResultTypeSelector } from './result_type_selector'; import { alertingApiProvider } from '../application/services/ml_api_service/alerting'; import { PreviewAlertCondition } from './preview_alert_condition'; -import { ANOMALY_THRESHOLD } from '../../common'; import { MlAnomalyDetectionAlertAdvancedSettings, MlAnomalyDetectionAlertParams, } from '../../common/types/alerts'; -import { ANOMALY_RESULT_TYPE } from '../../common/constants/anomalies'; import { InterimResultsControl } from './interim_results_control'; import { ConfigValidator } from './config_validator'; import { CombinedJobWithStats } from '../../common/types/anomaly_detection_jobs'; @@ -88,11 +87,11 @@ const MlAnomalyAlertTrigger: FC = ({ }, [jobsAndGroupIds]); const availableResultTypes = useMemo(() => { - if (jobConfigs.length === 0) return Object.values(ANOMALY_RESULT_TYPE); + if (jobConfigs.length === 0) return Object.values(ML_ANOMALY_RESULT_TYPE); return (jobConfigs ?? []).some((v) => Boolean(v.analysis_config?.influencers?.length)) - ? Object.values(ANOMALY_RESULT_TYPE) - : [ANOMALY_RESULT_TYPE.BUCKET, ANOMALY_RESULT_TYPE.RECORD]; + ? Object.values(ML_ANOMALY_RESULT_TYPE) + : [ML_ANOMALY_RESULT_TYPE.BUCKET, ML_ANOMALY_RESULT_TYPE.RECORD]; }, [jobConfigs]); useEffect( @@ -109,8 +108,8 @@ const MlAnomalyAlertTrigger: FC = ({ if (Object.keys(rest).length === 0) { setRuleProperty('params', { // Set defaults - severity: ANOMALY_THRESHOLD.CRITICAL, - resultType: ANOMALY_RESULT_TYPE.BUCKET, + severity: ML_ANOMALY_THRESHOLD.CRITICAL, + resultType: ML_ANOMALY_RESULT_TYPE.BUCKET, includeInterim: false, // Preserve job selection jobSelection, diff --git a/x-pack/plugins/ml/public/alerting/result_type_selector.tsx b/x-pack/plugins/ml/public/alerting/result_type_selector.tsx index 892eb8ada73dc..98a1fdca91422 100644 --- a/x-pack/plugins/ml/public/alerting/result_type_selector.tsx +++ b/x-pack/plugins/ml/public/alerting/result_type_selector.tsx @@ -8,13 +8,12 @@ import { EuiCard, EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import React, { FC, useMemo } from 'react'; -import { ANOMALY_RESULT_TYPE } from '../../common/constants/anomalies'; -import { AnomalyResultType } from '../../common/types/anomalies'; +import { type MlAnomalyResultType, ML_ANOMALY_RESULT_TYPE } from '@kbn/ml-anomaly-utils'; export interface ResultTypeSelectorProps { - value: AnomalyResultType | undefined; - availableOption: AnomalyResultType[]; - onChange: (value: AnomalyResultType) => void; + value: MlAnomalyResultType | undefined; + availableOption: MlAnomalyResultType[]; + onChange: (value: MlAnomalyResultType) => void; } export const ResultTypeSelector: FC = React.memo( @@ -22,7 +21,7 @@ export const ResultTypeSelector: FC = React.memo( const resultTypeOptions = useMemo(() => { return [ { - value: ANOMALY_RESULT_TYPE.BUCKET, + value: ML_ANOMALY_RESULT_TYPE.BUCKET, title: , description: ( = React.memo( ), }, { - value: ANOMALY_RESULT_TYPE.RECORD, + value: ML_ANOMALY_RESULT_TYPE.RECORD, title: , description: ( = React.memo( ), }, { - value: ANOMALY_RESULT_TYPE.INFLUENCER, + value: ML_ANOMALY_RESULT_TYPE.INFLUENCER, title: ( { if (this.state.applyAnnotationToSeries && chartDetails?.entityData?.entities) { chartDetails.entityData.entities.forEach((entity: Entity) => { const { fieldName, fieldValue } = entity; - const fieldType = entity.fieldType as PartitionFieldsType; + const fieldType = entity.fieldType as MlPartitionFieldsType; annotation[getAnnotationFieldName(fieldType)] = fieldName; annotation[getAnnotationFieldValue(fieldType)] = fieldValue; }); @@ -228,7 +227,7 @@ export class AnnotationFlyoutUI extends Component { // if unchecked, remove all the partitions before indexing if (!this.state.applyAnnotationToSeries) { delete annotation.detector_index; - PARTITION_FIELDS.forEach((fieldType) => { + ML_PARTITION_FIELDS.forEach((fieldType) => { delete annotation[getAnnotationFieldName(fieldType)]; delete annotation[getAnnotationFieldValue(fieldType)]; }); diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js b/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js index c8381726ee86a..c7ba114c0dfc5 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js @@ -27,7 +27,7 @@ import { InfluencersCell } from './influencers_cell'; import { LinksMenu } from './links_menu'; import { checkPermission } from '../../capabilities/check_capabilities'; import { mlFieldFormatService } from '../../services/field_format_service'; -import { isRuleSupported, isMultiBucketAnomaly } from '../../../../common/util/anomaly_utils'; +import { isRuleSupported, isMultiBucketAnomaly } from '@kbn/ml-anomaly-utils'; import { formatValue } from '../../formatters/format_value'; import { INFLUENCERS_LIMIT, ANOMALIES_TABLE_TABS } from './anomalies_table_constants'; import { SeverityCell } from './severity_cell'; diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/anomaly_details.tsx b/x-pack/plugins/ml/public/application/components/anomalies_table/anomaly_details.tsx index 10cba37c6e282..556dd5c810d84 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/anomaly_details.tsx +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/anomaly_details.tsx @@ -27,11 +27,10 @@ import { useEuiTheme, } from '@elastic/eui'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { getSeverity, type MlAnomaliesTableRecordExtended } from '@kbn/ml-anomaly-utils'; -import { getSeverity } from '../../../../common/util/anomaly_utils'; import { MAX_CHARS } from './anomalies_table_constants'; import type { CategoryDefinition } from '../../services/ml_api_service/results'; -import { AnomaliesTableRecordExtended } from '../../../../common/types/anomalies'; import { EntityCellFilter } from '../entity_cell'; import { ExplorerJob } from '../../explorer/explorer_utils'; @@ -42,7 +41,7 @@ import { } from './anomaly_details_utils'; interface Props { - anomaly: AnomaliesTableRecordExtended; + anomaly: MlAnomaliesTableRecordExtended; examples: string[]; definition: CategoryDefinition; isAggregatedData: boolean; @@ -118,7 +117,7 @@ export const AnomalyDetails: FC = ({ }; const Contents: FC<{ - anomaly: AnomaliesTableRecordExtended; + anomaly: MlAnomaliesTableRecordExtended; isAggregatedData: boolean; filter: EntityCellFilter; influencersLimit: number; @@ -166,7 +165,7 @@ const Contents: FC<{ ); }; -const Description: FC<{ anomaly: AnomaliesTableRecordExtended }> = ({ anomaly }) => { +const Description: FC<{ anomaly: MlAnomaliesTableRecordExtended }> = ({ anomaly }) => { const source = anomaly.source; let anomalyDescription = i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.anomalyInLabel', { @@ -235,7 +234,7 @@ const Description: FC<{ anomaly: AnomaliesTableRecordExtended }> = ({ anomaly }) }; const Details: FC<{ - anomaly: AnomaliesTableRecordExtended; + anomaly: MlAnomaliesTableRecordExtended; isAggregatedData: boolean; filter: EntityCellFilter; job: ExplorerJob; @@ -280,7 +279,7 @@ const Details: FC<{ }; const Influencers: FC<{ - anomaly: AnomaliesTableRecordExtended; + anomaly: MlAnomaliesTableRecordExtended; influencersLimit: number; influencerFilter: EntityCellFilter; }> = ({ anomaly, influencersLimit, influencerFilter }) => { diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/anomaly_details_utils.tsx b/x-pack/plugins/ml/public/application/components/anomalies_table/anomaly_details_utils.tsx index fd798f53fd68b..a9e33df7140d2 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/anomaly_details_utils.tsx +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/anomaly_details_utils.tsx @@ -18,19 +18,17 @@ import { EuiSpacer, EuiLink, } from '@elastic/eui'; -import { EntityCell, EntityCellFilter } from '../entity_cell'; -import { formatHumanReadableDateTimeSeconds } from '../../../../common/util/date_utils'; import { + getAnomalyScoreExplanationImpactValue, + getSeverityColor, showActualForFunction, showTypicalForFunction, -} from '../../../../common/util/anomaly_utils'; -import { AnomaliesTableRecord, MLAnomalyDoc } from '../../../../common/types/anomalies'; -import { formatValue } from '../../formatters/format_value'; +} from '@kbn/ml-anomaly-utils'; +import type { MlAnomaliesTableRecord, MLAnomalyDoc } from '@kbn/ml-anomaly-utils'; import { ML_JOB_AGGREGATION } from '../../../../common/constants/aggregation_types'; -import { - getAnomalyScoreExplanationImpactValue, - getSeverityColor, -} from '../../../../common/util/anomaly_utils'; +import { EntityCell, EntityCellFilter } from '../entity_cell'; +import { formatHumanReadableDateTimeSeconds } from '../../../../common/util/date_utils'; +import { formatValue } from '../../formatters/format_value'; import { useMlKibana } from '../../contexts/kibana'; const TIME_FIELD_NAME = 'timestamp'; @@ -68,7 +66,7 @@ export function getInfluencersItems( } export const DetailsItems: FC<{ - anomaly: AnomaliesTableRecord; + anomaly: MlAnomaliesTableRecord; filter: EntityCellFilter; modelPlotEnabled: boolean; }> = ({ anomaly, filter, modelPlotEnabled }) => { @@ -328,7 +326,7 @@ export const DetailsItems: FC<{ ); }; -export const AnomalyExplanationDetails: FC<{ anomaly: AnomaliesTableRecord }> = ({ anomaly }) => { +export const AnomalyExplanationDetails: FC<{ anomaly: MlAnomaliesTableRecord }> = ({ anomaly }) => { const { services: { docLinks }, } = useMlKibana(); diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/get_query_string_for_influencers.ts b/x-pack/plugins/ml/public/application/components/anomalies_table/get_query_string_for_influencers.ts index 670f7d83333ae..87d0a7b8f3117 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/get_query_string_for_influencers.ts +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/get_query_string_for_influencers.ts @@ -5,10 +5,10 @@ * 2.0. */ -import type { AnomaliesTableRecord } from '../../../../common/types/anomalies'; +import type { MlAnomaliesTableRecord } from '@kbn/ml-anomaly-utils'; export function getQueryStringForInfluencers( - influencers: AnomaliesTableRecord['influencers'] = [], + influencers: MlAnomaliesTableRecord['influencers'] = [], entityName?: string ) { return influencers diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.tsx b/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.tsx index d5bb4a6f8b469..63b87ec9cb246 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.tsx +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.tsx @@ -22,6 +22,13 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { ES_FIELD_TYPES } from '@kbn/field-types'; import { MAPS_APP_LOCATOR } from '@kbn/maps-plugin/public'; +import { + isCategorizationAnomaly, + isRuleSupported, + type MlCustomUrlAnomalyRecordDoc, + type MlKibanaUrlConfig, + type MlAnomaliesTableRecord, +} from '@kbn/ml-anomaly-utils'; import { mlJobService } from '../../services/job_service'; import { getDataViewIdFromName } from '../../util/index_utils'; import { getInitialAnomaliesLayers, getInitialSourceIndexFieldLayers } from '../../../maps/util'; @@ -41,27 +48,21 @@ import { getDateFormatTz, SourceIndicesWithGeoFields, } from '../../explorer/explorer_utils'; -import { isCategorizationAnomaly, isRuleSupported } from '../../../../common/util/anomaly_utils'; import { checkPermission } from '../../capabilities/check_capabilities'; -import type { - CustomUrlAnomalyRecordDoc, - KibanaUrlConfig, -} from '../../../../common/types/custom_urls'; import type { TimeRangeBounds } from '../../util/time_buckets'; import { useMlKibana } from '../../contexts/kibana'; // @ts-ignore import { getFieldTypeFromMapping } from '../../services/mapping_service'; -import type { AnomaliesTableRecord } from '../../../../common/types/anomalies'; import { getQueryStringForInfluencers } from './get_query_string_for_influencers'; import { getFiltersForDSLQuery } from '../../../../common/util/job_utils'; interface LinksMenuProps { - anomaly: AnomaliesTableRecord; + anomaly: MlAnomaliesTableRecord; bounds: TimeRangeBounds; showMapsLink: boolean; showViewSeriesLink: boolean; isAggregatedData: boolean; interval: 'day' | 'hour' | 'second'; - showRuleEditorFlyout: (anomaly: AnomaliesTableRecord) => void; + showRuleEditorFlyout: (anomaly: MlAnomaliesTableRecord) => void; onItemClick: () => void; sourceIndicesWithGeoFields: SourceIndicesWithGeoFields; } @@ -83,7 +84,7 @@ export const LinksMenuUI = (props: LinksMenuProps) => { return mlJobService.getJob(props.anomaly.jobId); }, [props.anomaly.jobId]); - const getAnomaliesMapsLink = async (anomaly: AnomaliesTableRecord) => { + const getAnomaliesMapsLink = async (anomaly: MlAnomaliesTableRecord) => { const index = job.datafeed_config.indices[0]; const dataViewId = await getDataViewIdFromName(index); @@ -121,7 +122,7 @@ export const LinksMenuUI = (props: LinksMenuProps) => { }; const getAnomalySourceMapsLink = async ( - anomaly: AnomaliesTableRecord, + anomaly: MlAnomaliesTableRecord, sourceIndicesWithGeoFields: SourceIndicesWithGeoFields ) => { const index = job.datafeed_config.indices[0]; @@ -287,7 +288,7 @@ export const LinksMenuUI = (props: LinksMenuProps) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [JSON.stringify(props.anomaly)]); - const openCustomUrl = (customUrl: KibanaUrlConfig) => { + const openCustomUrl = (customUrl: MlKibanaUrlConfig) => { const { anomaly, interval, isAggregatedData } = props; // eslint-disable-next-line no-console @@ -295,7 +296,7 @@ export const LinksMenuUI = (props: LinksMenuProps) => { // If url_value contains $earliest$ and $latest$ tokens, add in times to the source record. // Create a copy of the record as we are adding properties into it. - const record = cloneDeep(anomaly.source) as CustomUrlAnomalyRecordDoc; + const record = cloneDeep(anomaly.source) as MlCustomUrlAnomalyRecordDoc; const timestamp = record.timestamp; const configuredUrlValue = customUrl.url_value; const timeRangeInterval = @@ -376,7 +377,7 @@ export const LinksMenuUI = (props: LinksMenuProps) => { } else { // Replace any tokens in the configured url_value with values from the source record, // and then open link in a new tab/window. - const urlPath = getUrlForRecord(customUrl, record as CustomUrlAnomalyRecordDoc); + const urlPath = getUrlForRecord(customUrl, record as MlCustomUrlAnomalyRecordDoc); openCustomUrlWindow(urlPath, customUrl, basePath); } }; diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/severity_cell/severity_cell.tsx b/x-pack/plugins/ml/public/application/components/anomalies_table/severity_cell/severity_cell.tsx index 555110e4a3e4d..8342598835410 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/severity_cell/severity_cell.tsx +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/severity_cell/severity_cell.tsx @@ -7,10 +7,7 @@ import React, { FC, memo } from 'react'; import { EuiHealth, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { - getSeverityColor, - getFormattedSeverityScore, -} from '../../../../../common/util/anomaly_utils'; +import { getSeverityColor, getFormattedSeverityScore } from '@kbn/ml-anomaly-utils'; interface SeverityCellProps { /** diff --git a/x-pack/plugins/ml/public/application/components/controls/select_severity/select_severity.tsx b/x-pack/plugins/ml/public/application/components/controls/select_severity/select_severity.tsx index dfc95c9f0659a..53ce6b5d1cb3c 100644 --- a/x-pack/plugins/ml/public/application/components/controls/select_severity/select_severity.tsx +++ b/x-pack/plugins/ml/public/application/components/controls/select_severity/select_severity.tsx @@ -15,9 +15,7 @@ import { EuiHealth, EuiSpacer, EuiSuperSelect, EuiText, EuiSuperSelectProps } fr import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { usePageUrlState } from '@kbn/ml-url-state'; - -import { getSeverityColor } from '../../../../../common/util/anomaly_utils'; -import { ANOMALY_THRESHOLD } from '../../../../../common'; +import { getSeverityColor, ML_ANOMALY_THRESHOLD } from '@kbn/ml-anomaly-utils'; const warningLabel: string = i18n.translate('xpack.ml.controls.selectSeverity.warningLabel', { defaultMessage: 'warning', @@ -33,10 +31,10 @@ const criticalLabel: string = i18n.translate('xpack.ml.controls.selectSeverity.c }); const optionsMap = { - [warningLabel]: ANOMALY_THRESHOLD.LOW, - [minorLabel]: ANOMALY_THRESHOLD.MINOR, - [majorLabel]: ANOMALY_THRESHOLD.MAJOR, - [criticalLabel]: ANOMALY_THRESHOLD.CRITICAL, + [warningLabel]: ML_ANOMALY_THRESHOLD.LOW, + [minorLabel]: ML_ANOMALY_THRESHOLD.MINOR, + [majorLabel]: ML_ANOMALY_THRESHOLD.MAJOR, + [criticalLabel]: ML_ANOMALY_THRESHOLD.CRITICAL, }; export interface TableSeverityPageUrlState { @@ -52,24 +50,24 @@ export interface TableSeverity { export const SEVERITY_OPTIONS: TableSeverity[] = [ { - val: ANOMALY_THRESHOLD.LOW, + val: ML_ANOMALY_THRESHOLD.LOW, display: warningLabel, - color: getSeverityColor(ANOMALY_THRESHOLD.LOW), + color: getSeverityColor(ML_ANOMALY_THRESHOLD.LOW), }, { - val: ANOMALY_THRESHOLD.MINOR, + val: ML_ANOMALY_THRESHOLD.MINOR, display: minorLabel, - color: getSeverityColor(ANOMALY_THRESHOLD.MINOR), + color: getSeverityColor(ML_ANOMALY_THRESHOLD.MINOR), }, { - val: ANOMALY_THRESHOLD.MAJOR, + val: ML_ANOMALY_THRESHOLD.MAJOR, display: majorLabel, - color: getSeverityColor(ANOMALY_THRESHOLD.MAJOR), + color: getSeverityColor(ML_ANOMALY_THRESHOLD.MAJOR), }, { - val: ANOMALY_THRESHOLD.CRITICAL, + val: ML_ANOMALY_THRESHOLD.CRITICAL, display: criticalLabel, - color: getSeverityColor(ANOMALY_THRESHOLD.CRITICAL), + color: getSeverityColor(ML_ANOMALY_THRESHOLD.CRITICAL), }, ]; diff --git a/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/editor.tsx b/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/editor.tsx index 523f59c32f224..45ccd9bc7ecdd 100644 --- a/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/editor.tsx +++ b/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/editor.tsx @@ -27,13 +27,13 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { DataViewListItem } from '@kbn/data-views-plugin/common'; import { DataView } from '@kbn/data-views-plugin/public'; +import type { MlUrlConfig } from '@kbn/ml-anomaly-utils'; import { CustomUrlSettings, isValidCustomUrlSettingsTimeRange } from './utils'; import { isValidLabel } from '../../../util/custom_url_utils'; import { type DataFrameAnalyticsConfig } from '../../../../../common/types/data_frame_analytics'; import { type Job } from '../../../../../common/types/anomaly_detection_jobs'; import { TIME_RANGE_TYPE, TimeRangeType, URL_TYPE } from './constants'; -import { UrlConfig } from '../../../../../common/types/custom_urls'; import { CustomTimeRangePicker } from './custom_time_range_picker'; import { useMlKibana } from '../../../contexts/kibana'; import { getDropDownOptions } from './get_dropdown_options'; @@ -64,7 +64,7 @@ function getLinkToOptions() { interface CustomUrlEditorProps { customUrl: CustomUrlSettings | undefined; setEditCustomUrl: (url: CustomUrlSettings) => void; - savedCustomUrls: UrlConfig[]; + savedCustomUrls: MlUrlConfig[]; dashboards: Array<{ id: string; title: string }>; dataViewListItems: DataViewListItem[]; showTimeRangeSelector?: boolean; diff --git a/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/list.tsx b/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/list.tsx index b9a299b17edab..33ab2765f7fa0 100644 --- a/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/list.tsx +++ b/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/list.tsx @@ -20,6 +20,7 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import type { MlUrlConfig, MlKibanaUrlConfig } from '@kbn/ml-anomaly-utils'; import { useMlKibana } from '../../../contexts/kibana'; import { isValidLabel, openCustomUrlWindow } from '../../../util/custom_url_utils'; import { getTestUrl } from './utils'; @@ -27,10 +28,9 @@ import { getTestUrl } from './utils'; import { parseInterval } from '../../../../../common/util/parse_interval'; import { type DataFrameAnalyticsConfig } from '../../../../../common/types/data_frame_analytics'; import { TIME_RANGE_TYPE } from './constants'; -import { UrlConfig, KibanaUrlConfig } from '../../../../../common/types/custom_urls'; import { Job, isAnomalyDetectionJob } from '../../../../../common/types/anomaly_detection_jobs'; -function isValidTimeRange(timeRange: KibanaUrlConfig['time_range']): boolean { +function isValidTimeRange(timeRange: MlKibanaUrlConfig['time_range']): boolean { // Allow empty timeRange string, which gives the 'auto' behaviour. if (timeRange === undefined || timeRange.length === 0 || timeRange === TIME_RANGE_TYPE.AUTO) { return true; @@ -42,8 +42,8 @@ function isValidTimeRange(timeRange: KibanaUrlConfig['time_range']): boolean { export interface CustomUrlListProps { job: Job | DataFrameAnalyticsConfig; - customUrls: UrlConfig[]; - onChange: (customUrls: UrlConfig[]) => void; + customUrls: MlUrlConfig[]; + onChange: (customUrls: MlUrlConfig[]) => void; } /* @@ -91,9 +91,9 @@ export const CustomUrlList: FC = ({ const timeRange = e.target.value; if (timeRange !== undefined && timeRange.length > 0) { - (customUrls[index] as KibanaUrlConfig).time_range = timeRange; + (customUrls[index] as MlKibanaUrlConfig).time_range = timeRange; } else { - delete (customUrls[index] as KibanaUrlConfig).time_range; + delete (customUrls[index] as MlKibanaUrlConfig).time_range; } setCustomUrls([...customUrls]); } @@ -144,7 +144,7 @@ export const CustomUrlList: FC = ({ : []; // Validate the time range. - const timeRange = (customUrl as KibanaUrlConfig).time_range; + const timeRange = (customUrl as MlKibanaUrlConfig).time_range; const isInvalidTimeRange = !isValidTimeRange(timeRange); const invalidIntervalError = isInvalidTimeRange ? [ @@ -223,7 +223,7 @@ export const CustomUrlList: FC = ({ isInvalid={isInvalidTimeRange} > onTimeRangeChange(e, index)} diff --git a/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/utils.ts b/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/utils.ts index f8ee16456e92a..31c4f81545dcb 100644 --- a/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/utils.ts +++ b/x-pack/plugins/ml/public/application/components/custom_urls/custom_url_editor/utils.ts @@ -16,12 +16,12 @@ import type { DataView, DataViewField } from '@kbn/data-views-plugin/common'; import { isFilterPinned, Filter } from '@kbn/es-query'; import { DataViewListItem } from '@kbn/data-views-plugin/common'; import { TimeRange as EsQueryTimeRange } from '@kbn/es-query'; +import type { MlKibanaUrlConfig, MlUrlConfig } from '@kbn/ml-anomaly-utils'; import { DEFAULT_RESULTS_FIELD } from '../../../../../common/constants/data_frame_analytics'; import { isDataFrameAnalyticsConfigs, type DataFrameAnalyticsConfig, } from '../../../../../common/types/data_frame_analytics'; -import { KibanaUrlConfig } from '../../../../../common/types/custom_urls'; import { categoryFieldTypes } from '../../../../../common/util/fields_utils'; import { TIME_RANGE_TYPE, URL_TYPE } from './constants'; @@ -39,7 +39,6 @@ import { ml } from '../../../services/ml_api_service'; import { escapeForElasticsearchQuery } from '../../../util/string_utils'; import { getSavedObjectsClient, getDashboard } from '../../../util/dependency_cache'; -import { UrlConfig } from '../../../../../common/types/custom_urls'; import { CombinedJob, Job, @@ -197,7 +196,7 @@ export function isValidCustomUrlSettingsTimeRange(timeRangeSettings: TimeRange): export function isValidCustomUrlSettings( settings: CustomUrlSettings, - savedCustomUrls: UrlConfig[] + savedCustomUrls: MlUrlConfig[] ): boolean { let isValid = isValidLabel(settings.label, savedCustomUrls); if (isValid === true) { @@ -206,7 +205,7 @@ export function isValidCustomUrlSettings( return isValid; } -export function buildCustomUrlFromSettings(settings: CustomUrlSettings): Promise { +export function buildCustomUrlFromSettings(settings: CustomUrlSettings): Promise { // Dashboard URL returns a Promise as a query is made to obtain the full dashboard config. // So wrap the other two return types in a Promise for consistent return type. if (settings.type === URL_TYPE.KIBANA_DASHBOARD) { @@ -237,7 +236,7 @@ function getUrlRangeFromSettings(settings: CustomUrlSettings) { }; } -async function buildDashboardUrlFromSettings(settings: CustomUrlSettings): Promise { +async function buildDashboardUrlFromSettings(settings: CustomUrlSettings): Promise { // Get the complete list of attributes for the selected dashboard (query, filters). const { dashboardId, queryFieldNames } = settings.kibanaSettings ?? {}; @@ -352,7 +351,7 @@ function buildDiscoverUrlFromSettings(settings: CustomUrlSettings) { const urlValue = `discover#/?_g=${_g}&_a=${_a}`; - const urlToAdd: KibanaUrlConfig = { + const urlToAdd: MlKibanaUrlConfig = { url_name: settings.label, url_value: urlValue, time_range: TIME_RANGE_TYPE.AUTO, @@ -389,7 +388,7 @@ function buildAppStateQueryParam(queryFieldNames: string[]) { // Builds the full URL for testing out a custom URL configuration, which // may contain dollar delimited partition / influencer entity tokens and // drilldown time range settings. -async function getAnomalyDetectionJobTestUrl(job: Job, customUrl: UrlConfig): Promise { +async function getAnomalyDetectionJobTestUrl(job: Job, customUrl: MlUrlConfig): Promise { const interval = parseInterval(job.analysis_config.bucket_span); const bucketSpanSecs = interval !== null ? interval.asSeconds() : 0; @@ -479,7 +478,7 @@ async function getAnomalyDetectionJobTestUrl(job: Job, customUrl: UrlConfig): Pr async function getDataFrameAnalyticsTestUrl( job: DataFrameAnalyticsConfig, - customUrl: UrlConfig, + customUrl: MlUrlConfig, currentTimeFilter?: EsQueryTimeRange ): Promise { // By default, return configured url_value. Look to substitute any dollar-delimited @@ -522,7 +521,7 @@ async function getDataFrameAnalyticsTestUrl( export function getTestUrl( job: Job | DataFrameAnalyticsConfig, - customUrl: UrlConfig, + customUrl: MlUrlConfig, currentTimeFilter?: EsQueryTimeRange ) { if (isDataFrameAnalyticsConfigs(job)) { diff --git a/x-pack/plugins/ml/public/application/components/custom_urls/custom_urls.tsx b/x-pack/plugins/ml/public/application/components/custom_urls/custom_urls.tsx index 4f9ad5245cf91..aed4ed646972c 100644 --- a/x-pack/plugins/ml/public/application/components/custom_urls/custom_urls.tsx +++ b/x-pack/plugins/ml/public/application/components/custom_urls/custom_urls.tsx @@ -26,6 +26,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { withKibana } from '@kbn/kibana-react-plugin/public'; import { DataViewListItem } from '@kbn/data-views-plugin/common'; +import type { MlUrlConfig } from '@kbn/ml-anomaly-utils'; import { MlKibanaReactContextValue } from '../../contexts/kibana'; import { CustomUrlEditor, CustomUrlList } from './custom_url_editor'; import { @@ -40,7 +41,6 @@ import { loadDataViewListItems, } from '../../jobs/jobs_list/components/edit_job_flyout/edit_utils'; import { openCustomUrlWindow } from '../../util/custom_url_utils'; -import { UrlConfig } from '../../../../common/types/custom_urls'; import type { CustomUrlsWrapperProps } from './custom_urls_wrapper'; import { isAnomalyDetectionJob } from '../../../../common/types/anomaly_detection_jobs'; import { isDataFrameAnalyticsConfigs } from '../../../../common/types/data_frame_analytics'; @@ -48,7 +48,7 @@ import { isDataFrameAnalyticsConfigs } from '../../../../common/types/data_frame const MAX_NUMBER_DASHBOARDS = 1000; interface CustomUrlsState { - customUrls: UrlConfig[]; + customUrls: MlUrlConfig[]; dashboards: Array<{ id: string; title: string }>; dataViewListItems: DataViewListItem[]; editorOpen: boolean; diff --git a/x-pack/plugins/ml/public/application/components/custom_urls/custom_urls_wrapper.tsx b/x-pack/plugins/ml/public/application/components/custom_urls/custom_urls_wrapper.tsx index 175b16dca74ea..b24ad3d5e7e05 100644 --- a/x-pack/plugins/ml/public/application/components/custom_urls/custom_urls_wrapper.tsx +++ b/x-pack/plugins/ml/public/application/components/custom_urls/custom_urls_wrapper.tsx @@ -6,16 +6,16 @@ */ import React, { FC } from 'react'; +import type { MlUrlConfig } from '@kbn/ml-anomaly-utils'; import { useMlKibana } from '../../contexts/kibana'; import { Job } from '../../../../common/types/anomaly_detection_jobs'; -import { UrlConfig } from '../../../../common/types/custom_urls'; import { type DataFrameAnalyticsConfig } from '../../../../common/types/data_frame_analytics'; import { CustomUrls } from './custom_urls'; export interface CustomUrlsWrapperProps { job: Job | DataFrameAnalyticsConfig; - jobCustomUrls: UrlConfig[]; - setCustomUrls: (customUrls: UrlConfig[]) => void; + jobCustomUrls: MlUrlConfig[]; + setCustomUrls: (customUrls: MlUrlConfig[]) => void; editMode?: 'inline' | 'modal'; } diff --git a/x-pack/plugins/ml/public/application/components/custom_urls/is_valid_custom_urls.ts b/x-pack/plugins/ml/public/application/components/custom_urls/is_valid_custom_urls.ts index ed45f966a54eb..d1509ef32dd99 100644 --- a/x-pack/plugins/ml/public/application/components/custom_urls/is_valid_custom_urls.ts +++ b/x-pack/plugins/ml/public/application/components/custom_urls/is_valid_custom_urls.ts @@ -5,10 +5,10 @@ * 2.0. */ +import { type MlUrlConfig, isMlKibanaUrlConfigWithTimeRange } from '@kbn/ml-anomaly-utils'; import { isValidLabel, isValidTimeRange } from '../../util/custom_url_utils'; -import { UrlConfig, isKibanaUrlConfigWithTimeRange } from '../../../../common/types/custom_urls'; -export function isValidCustomUrls(customUrls: UrlConfig[]) { +export function isValidCustomUrls(customUrls: MlUrlConfig[]) { if (customUrls === undefined || customUrls.length === 0) { return true; } @@ -20,7 +20,7 @@ export function isValidCustomUrls(customUrls: UrlConfig[]) { const otherUrls = [...customUrls]; otherUrls.splice(index, 1); // Don't compare label with itself. let itemValid = isValidLabel(label, otherUrls); - if (itemValid === true && isKibanaUrlConfigWithTimeRange(customUrl)) { + if (itemValid === true && isMlKibanaUrlConfigWithTimeRange(customUrl)) { // Validate the time range. const timeRange = customUrl.time_range; itemValid = isValidTimeRange(timeRange); diff --git a/x-pack/plugins/ml/public/application/components/entity_cell/entity_cell.tsx b/x-pack/plugins/ml/public/application/components/entity_cell/entity_cell.tsx index defe64583e927..3e3ca7d4729ca 100644 --- a/x-pack/plugins/ml/public/application/components/entity_cell/entity_cell.tsx +++ b/x-pack/plugins/ml/public/application/components/entity_cell/entity_cell.tsx @@ -10,9 +10,9 @@ import React, { FC } from 'react'; import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiText, EuiToolTip } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; +import { ML_ENTITY_FIELD_OPERATIONS } from '@kbn/ml-anomaly-utils'; import { EMPTY_FIELD_VALUE_LABEL } from '../../timeseriesexplorer/components/entity_control/entity_control'; import { MLCATEGORY } from '../../../../common/constants/field_types'; -import { ENTITY_FIELD_OPERATIONS } from '../../../../common/util/anomaly_utils'; import { blurButtonOnClick } from '../../util/component_utils'; export type EntityCellFilter = ( @@ -44,7 +44,7 @@ function getAddFilter({ entityName, entityValue, filter }: EntityCellProps) { data-test-subj={`mlAnomaliesTableEntityCellAddFilterButton-${entityValue}`} className="filter-button" onClick={blurButtonOnClick(() => { - filter(entityName, entityValue, ENTITY_FIELD_OPERATIONS.ADD); + filter(entityName, entityValue, ML_ENTITY_FIELD_OPERATIONS.ADD); })} iconType="plusInCircle" aria-label={i18n.translate('xpack.ml.anomaliesTable.entityCell.addFilterAriaLabel', { @@ -72,7 +72,7 @@ function getRemoveFilter({ entityName, entityValue, filter }: EntityCellProps) { data-test-subj={`mlAnomaliesTableEntityCellRemoveFilterButton-${entityValue}`} className="filter-button" onClick={blurButtonOnClick(() => { - filter(entityName, entityValue, ENTITY_FIELD_OPERATIONS.REMOVE); + filter(entityName, entityValue, ML_ENTITY_FIELD_OPERATIONS.REMOVE); })} iconType="minusInCircle" aria-label={i18n.translate('xpack.ml.anomaliesTable.entityCell.removeFilterAriaLabel', { diff --git a/x-pack/plugins/ml/public/application/components/influencers_list/influencers_list.tsx b/x-pack/plugins/ml/public/application/components/influencers_list/influencers_list.tsx index 456c8e8719dd3..e78d2a5fc68b9 100644 --- a/x-pack/plugins/ml/public/application/components/influencers_list/influencers_list.tsx +++ b/x-pack/plugins/ml/public/application/components/influencers_list/influencers_list.tsx @@ -14,8 +14,8 @@ import React, { FC } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle, EuiToolTip } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import { getSeverity, getFormattedSeverityScore } from '@kbn/ml-anomaly-utils'; import { abbreviateWholeNumber } from '../../formatters/abbreviate_whole_number'; -import { getSeverity, getFormattedSeverityScore } from '../../../../common/util/anomaly_utils'; import { EntityCell, EntityCellFilter } from '../entity_cell'; export interface InfluencerValueData { diff --git a/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/chart_loader.ts b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/chart_loader.ts index 790b69f7ebdd0..c6364b93fbf49 100644 --- a/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/chart_loader.ts +++ b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/chart_loader.ts @@ -5,9 +5,9 @@ * 2.0. */ +import { getSeverityType } from '@kbn/ml-anomaly-utils'; import { MlResultsService } from '../../../services/results_service'; import { CombinedJobWithStats } from '../../../../../common/types/anomaly_detection_jobs'; -import { getSeverityType } from '../../../../../common/util/anomaly_utils'; import { Anomaly } from '../../../jobs/new_job/common/results_loader/results_loader'; import { LineChartPoint } from '../../../jobs/new_job/common/chart_loader/chart_loader'; diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/actions_section.js b/x-pack/plugins/ml/public/application/components/rule_editor/actions_section.js index 63dec27aabd46..8f45fa7f57364 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/actions_section.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/actions_section.js @@ -21,7 +21,7 @@ import { EuiText, } from '@elastic/eui'; -import { ACTION } from '../../../../common/constants/detector_rule'; +import { ML_DETECTOR_RULE_ACTION } from '@kbn/ml-anomaly-utils'; import { FormattedMessage } from '@kbn/i18n-react'; export function ActionsSection({ actions, onSkipResultChange, onSkipModelUpdateChange }) { @@ -46,7 +46,7 @@ export function ActionsSection({ actions, onSkipResultChange, onSkipModelUpdateC defaultMessage="Skip result (recommended)" /> } - checked={actions.indexOf(ACTION.SKIP_RESULT) > -1} + checked={actions.indexOf(ML_DETECTOR_RULE_ACTION.SKIP_RESULT) > -1} onChange={onSkipResultChange} /> @@ -77,7 +77,7 @@ export function ActionsSection({ actions, onSkipResultChange, onSkipModelUpdateC defaultMessage="Skip model update" /> } - checked={actions.indexOf(ACTION.SKIP_MODEL_UPDATE) > -1} + checked={actions.indexOf(ML_DETECTOR_RULE_ACTION.SKIP_MODEL_UPDATE) > -1} onChange={onSkipModelUpdateChange} /> diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/actions_section.test.js b/x-pack/plugins/ml/public/application/components/rule_editor/actions_section.test.js index 37315b61c2d02..b99a0066600f6 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/actions_section.test.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/actions_section.test.js @@ -5,11 +5,12 @@ * 2.0. */ -import { shallowWithIntl } from '@kbn/test-jest-helpers'; import React from 'react'; +import { shallowWithIntl } from '@kbn/test-jest-helpers'; +import { ML_DETECTOR_RULE_ACTION } from '@kbn/ml-anomaly-utils'; + import { ActionsSection } from './actions_section'; -import { ACTION } from '../../../../common/constants/detector_rule'; describe('ActionsSection', () => { const onSkipResultChange = jest.fn(() => {}); @@ -34,7 +35,7 @@ describe('ActionsSection', () => { test('renders with skip_result selected', () => { const props = { ...requiredProps, - actions: [ACTION.SKIP_RESULT], + actions: [ML_DETECTOR_RULE_ACTION.SKIP_RESULT], }; const component = shallowWithIntl(); @@ -45,7 +46,7 @@ describe('ActionsSection', () => { test('renders with skip_result and skip_model_update selected', () => { const component = shallowWithIntl( {}} onSkipModelUpdateChange={() => {}} /> diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/condition_expression.js b/x-pack/plugins/ml/public/application/components/rule_editor/condition_expression.js index a6e87e4bd5af2..3857a97c3c9e7 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/condition_expression.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/condition_expression.js @@ -23,24 +23,25 @@ import { EuiFieldNumber, } from '@elastic/eui'; -import { APPLIES_TO, OPERATOR } from '../../../../common/constants/detector_rule'; -import { appliesToText, operatorToText } from './utils'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { ML_DETECTOR_RULE_APPLIES_TO, ML_DETECTOR_RULE_OPERATOR } from '@kbn/ml-anomaly-utils'; + +import { appliesToText, operatorToText } from './utils'; export class ConditionExpression extends Component { static propTypes = { index: PropTypes.number.isRequired, appliesTo: PropTypes.oneOf([ - APPLIES_TO.ACTUAL, - APPLIES_TO.TYPICAL, - APPLIES_TO.DIFF_FROM_TYPICAL, + ML_DETECTOR_RULE_APPLIES_TO.ACTUAL, + ML_DETECTOR_RULE_APPLIES_TO.TYPICAL, + ML_DETECTOR_RULE_APPLIES_TO.DIFF_FROM_TYPICAL, ]), operator: PropTypes.oneOf([ - OPERATOR.LESS_THAN, - OPERATOR.LESS_THAN_OR_EQUAL, - OPERATOR.GREATER_THAN, - OPERATOR.GREATER_THAN_OR_EQUAL, + ML_DETECTOR_RULE_OPERATOR.LESS_THAN, + ML_DETECTOR_RULE_OPERATOR.LESS_THAN_OR_EQUAL, + ML_DETECTOR_RULE_OPERATOR.GREATER_THAN, + ML_DETECTOR_RULE_OPERATOR.GREATER_THAN_OR_EQUAL, ]), value: PropTypes.number.isRequired, updateCondition: PropTypes.func.isRequired, @@ -111,11 +112,17 @@ export class ConditionExpression extends Component { value={this.props.appliesTo} onChange={this.changeAppliesTo} options={[ - { value: APPLIES_TO.ACTUAL, text: appliesToText(APPLIES_TO.ACTUAL) }, - { value: APPLIES_TO.TYPICAL, text: appliesToText(APPLIES_TO.TYPICAL) }, { - value: APPLIES_TO.DIFF_FROM_TYPICAL, - text: appliesToText(APPLIES_TO.DIFF_FROM_TYPICAL), + value: ML_DETECTOR_RULE_APPLIES_TO.ACTUAL, + text: appliesToText(ML_DETECTOR_RULE_APPLIES_TO.ACTUAL), + }, + { + value: ML_DETECTOR_RULE_APPLIES_TO.TYPICAL, + text: appliesToText(ML_DETECTOR_RULE_APPLIES_TO.TYPICAL), + }, + { + value: ML_DETECTOR_RULE_APPLIES_TO.DIFF_FROM_TYPICAL, + text: appliesToText(ML_DETECTOR_RULE_APPLIES_TO.DIFF_FROM_TYPICAL), }, ]} /> @@ -140,15 +147,21 @@ export class ConditionExpression extends Component { value={this.props.operator} onChange={this.changeOperator} options={[ - { value: OPERATOR.LESS_THAN, text: operatorToText(OPERATOR.LESS_THAN) }, { - value: OPERATOR.LESS_THAN_OR_EQUAL, - text: operatorToText(OPERATOR.LESS_THAN_OR_EQUAL), + value: ML_DETECTOR_RULE_OPERATOR.LESS_THAN, + text: operatorToText(ML_DETECTOR_RULE_OPERATOR.LESS_THAN), + }, + { + value: ML_DETECTOR_RULE_OPERATOR.LESS_THAN_OR_EQUAL, + text: operatorToText(ML_DETECTOR_RULE_OPERATOR.LESS_THAN_OR_EQUAL), + }, + { + value: ML_DETECTOR_RULE_OPERATOR.GREATER_THAN, + text: operatorToText(ML_DETECTOR_RULE_OPERATOR.GREATER_THAN), }, - { value: OPERATOR.GREATER_THAN, text: operatorToText(OPERATOR.GREATER_THAN) }, { - value: OPERATOR.GREATER_THAN_OR_EQUAL, - text: operatorToText(OPERATOR.GREATER_THAN_OR_EQUAL), + value: ML_DETECTOR_RULE_OPERATOR.GREATER_THAN_OR_EQUAL, + text: operatorToText(ML_DETECTOR_RULE_OPERATOR.GREATER_THAN_OR_EQUAL), }, ]} /> diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/condition_expression.test.js b/x-pack/plugins/ml/public/application/components/rule_editor/condition_expression.test.js index 087a23b2fce90..a0f9eedddf5d6 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/condition_expression.test.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/condition_expression.test.js @@ -11,8 +11,9 @@ jest.mock('../../services/job_service', () => 'mlJobService'); import { shallowWithIntl } from '@kbn/test-jest-helpers'; import React from 'react'; +import { ML_DETECTOR_RULE_APPLIES_TO, ML_DETECTOR_RULE_OPERATOR } from '@kbn/ml-anomaly-utils'; + import { ConditionExpression } from './condition_expression'; -import { APPLIES_TO, OPERATOR } from '../../../../common/constants/detector_rule'; describe('ConditionExpression', () => { const updateCondition = jest.fn(() => {}); @@ -38,8 +39,8 @@ describe('ConditionExpression', () => { test('renders with appliesTo, operator and value supplied', () => { const props = { ...requiredProps, - appliesTo: APPLIES_TO.DIFF_FROM_TYPICAL, - operator: OPERATOR.GREATER_THAN, + appliesTo: ML_DETECTOR_RULE_APPLIES_TO.DIFF_FROM_TYPICAL, + operator: ML_DETECTOR_RULE_OPERATOR.GREATER_THAN, value: 123, }; diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/conditions_section.test.js b/x-pack/plugins/ml/public/application/components/rule_editor/conditions_section.test.js index 07e6282451b2b..01cf0a035e9d3 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/conditions_section.test.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/conditions_section.test.js @@ -11,9 +11,10 @@ jest.mock('../../services/job_service', () => 'mlJobService'); import { shallowWithIntl } from '@kbn/test-jest-helpers'; import React from 'react'; +import { ML_DETECTOR_RULE_APPLIES_TO, ML_DETECTOR_RULE_OPERATOR } from '@kbn/ml-anomaly-utils'; + import { ConditionsSection } from './conditions_section'; import { getNewConditionDefaults } from './utils'; -import { APPLIES_TO, OPERATOR } from '../../../../common/constants/detector_rule'; describe('ConditionsSectionExpression', () => { const addCondition = jest.fn(() => {}); @@ -21,8 +22,8 @@ describe('ConditionsSectionExpression', () => { const deleteCondition = jest.fn(() => {}); const testCondition = { - applies_to: APPLIES_TO.TYPICAL, - operator: OPERATOR.GREATER_THAN_OR_EQUAL, + applies_to: ML_DETECTOR_RULE_APPLIES_TO.TYPICAL, + operator: ML_DETECTOR_RULE_OPERATOR.GREATER_THAN_OR_EQUAL, value: 1.23, }; diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js b/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js index 668907c010108..907933ad290c3 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js @@ -31,7 +31,12 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { withKibana } from '@kbn/kibana-react-plugin/public'; import { extractErrorMessage } from '@kbn/ml-error-utils'; +import { + ML_DETECTOR_RULE_ACTION, + ML_DETECTOR_RULE_CONDITIONS_NOT_SUPPORTED_FUNCTIONS, +} from '@kbn/ml-anomaly-utils'; import { DetectorDescriptionList } from './components/detector_description_list'; import { ActionsSection } from './actions_section'; @@ -48,12 +53,7 @@ import { addItemToFilter, } from './utils'; -import { - ACTION, - CONDITIONS_NOT_SUPPORTED_FUNCTIONS, -} from '../../../../common/constants/detector_rule'; import { getPartitioningFieldNames } from '../../../../common/util/job_utils'; -import { withKibana } from '@kbn/kibana-react-plugin/public'; import { mlJobService } from '../../services/job_service'; import { ml } from '../../services/ml_api_service'; @@ -202,9 +202,9 @@ class RuleEditorFlyoutUI extends Component { const checked = e.target.checked; this.setState((prevState) => { const actions = [...prevState.rule.actions]; - const idx = actions.indexOf(ACTION.SKIP_RESULT); + const idx = actions.indexOf(ML_DETECTOR_RULE_ACTION.SKIP_RESULT); if (idx === -1 && checked) { - actions.push(ACTION.SKIP_RESULT); + actions.push(ML_DETECTOR_RULE_ACTION.SKIP_RESULT); } else if (idx > -1 && !checked) { actions.splice(idx, 1); } @@ -219,9 +219,9 @@ class RuleEditorFlyoutUI extends Component { const checked = e.target.checked; this.setState((prevState) => { const actions = [...prevState.rule.actions]; - const idx = actions.indexOf(ACTION.SKIP_MODEL_UPDATE); + const idx = actions.indexOf(ML_DETECTOR_RULE_ACTION.SKIP_MODEL_UPDATE); if (idx === -1 && checked) { - actions.push(ACTION.SKIP_MODEL_UPDATE); + actions.push(ML_DETECTOR_RULE_ACTION.SKIP_MODEL_UPDATE); } else if (idx > -1 && !checked) { actions.splice(idx, 1); } @@ -551,7 +551,7 @@ class RuleEditorFlyoutUI extends Component { const hasPartitioningFields = this.partitioningFieldNames && this.partitioningFieldNames.length > 0; const conditionSupported = - CONDITIONS_NOT_SUPPORTED_FUNCTIONS.indexOf(anomaly.source.function) === -1; + ML_DETECTOR_RULE_CONDITIONS_NOT_SUPPORTED_FUNCTIONS.indexOf(anomaly.source.function) === -1; const conditionsText = i18n.translate( 'xpack.ml.ruleEditor.ruleEditorFlyout.conditionsDescription', { diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/scope_expression.js b/x-pack/plugins/ml/public/application/components/rule_editor/scope_expression.js index e2e3782aeb82f..7286c2c9b9067 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/scope_expression.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/scope_expression.js @@ -22,10 +22,11 @@ import { EuiSelect, } from '@elastic/eui'; -import { FILTER_TYPE } from '../../../../common/constants/detector_rule'; -import { filterTypeToText } from './utils'; +import { ML_DETECTOR_RULE_FILTER_TYPE } from '@kbn/ml-anomaly-utils'; import { FormattedMessage } from '@kbn/i18n-react'; +import { filterTypeToText } from './utils'; + function getFilterListOptions(filterListIds) { return filterListIds.map((filterId) => ({ value: filterId, text: filterId })); } @@ -87,8 +88,14 @@ export class ScopeExpression extends Component { value={filterType} onChange={this.onChangeFilterType} options={[ - { value: FILTER_TYPE.INCLUDE, text: filterTypeToText(FILTER_TYPE.INCLUDE) }, - { value: FILTER_TYPE.EXCLUDE, text: filterTypeToText(FILTER_TYPE.EXCLUDE) }, + { + value: ML_DETECTOR_RULE_FILTER_TYPE.INCLUDE, + text: filterTypeToText(ML_DETECTOR_RULE_FILTER_TYPE.INCLUDE), + }, + { + value: ML_DETECTOR_RULE_FILTER_TYPE.EXCLUDE, + text: filterTypeToText(ML_DETECTOR_RULE_FILTER_TYPE.EXCLUDE), + }, ]} /> @@ -168,7 +175,10 @@ export class ScopeExpression extends Component { ScopeExpression.propTypes = { fieldName: PropTypes.string.isRequired, filterId: PropTypes.string, - filterType: PropTypes.oneOf([FILTER_TYPE.INCLUDE, FILTER_TYPE.EXCLUDE]), + filterType: PropTypes.oneOf([ + ML_DETECTOR_RULE_FILTER_TYPE.INCLUDE, + ML_DETECTOR_RULE_FILTER_TYPE.EXCLUDE, + ]), enabled: PropTypes.bool.isRequired, filterListIds: PropTypes.array.isRequired, updateScope: PropTypes.func.isRequired, diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/scope_expression.test.js b/x-pack/plugins/ml/public/application/components/rule_editor/scope_expression.test.js index 68ce65ea2c847..99212675829bb 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/scope_expression.test.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/scope_expression.test.js @@ -8,11 +8,12 @@ // Mock the mlJobService that is imported for saving rules. jest.mock('../../services/job_service', () => 'mlJobService'); -import { shallowWithIntl } from '@kbn/test-jest-helpers'; import React from 'react'; +import { shallowWithIntl } from '@kbn/test-jest-helpers'; +import { ML_DETECTOR_RULE_FILTER_TYPE } from '@kbn/ml-anomaly-utils'; + import { ScopeExpression } from './scope_expression'; -import { FILTER_TYPE } from '../../../../common/constants/detector_rule'; describe('ScopeExpression', () => { const testFilterListIds = ['web_domains', 'safe_domains', 'uk_domains']; @@ -52,7 +53,7 @@ describe('ScopeExpression', () => { ...requiredProps, filterListIds: testFilterListIds, filterId: 'safe_domains', - filterType: FILTER_TYPE.INCLUDE, + filterType: ML_DETECTOR_RULE_FILTER_TYPE.INCLUDE, enabled: true, }; @@ -66,7 +67,7 @@ describe('ScopeExpression', () => { ...requiredProps, filterListIds: testFilterListIds, filterId: 'safe_domains', - filterType: FILTER_TYPE.INCLUDE, + filterType: ML_DETECTOR_RULE_FILTER_TYPE.INCLUDE, enabled: false, }; diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/scope_section.test.js b/x-pack/plugins/ml/public/application/components/rule_editor/scope_section.test.js index 145f3d5d53256..05d480291185b 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/scope_section.test.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/scope_section.test.js @@ -16,11 +16,12 @@ jest.mock('../../capabilities/check_capabilities', () => ({ checkPermission: (privilege) => mockCheckPermission(privilege), })); -import { shallowWithIntl } from '@kbn/test-jest-helpers'; import React from 'react'; +import { shallowWithIntl } from '@kbn/test-jest-helpers'; +import { ML_DETECTOR_RULE_FILTER_TYPE } from '@kbn/ml-anomaly-utils'; + import { ScopeSection } from './scope_section'; -import { FILTER_TYPE } from '../../../../common/constants/detector_rule'; describe('ScopeSection', () => { const testFilterListIds = ['web_domains', 'safe_domains', 'uk_domains']; @@ -28,7 +29,7 @@ describe('ScopeSection', () => { const testScope = { domain: { filter_id: 'uk_domains', - filter_type: FILTER_TYPE.INCLUDE, + filter_type: ML_DETECTOR_RULE_FILTER_TYPE.INCLUDE, enabled: true, }, }; diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/select_rule_action/edit_condition_link.js b/x-pack/plugins/ml/public/application/components/rule_editor/select_rule_action/edit_condition_link.js index c97886ae99601..04d23affa1e2b 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/select_rule_action/edit_condition_link.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/select_rule_action/edit_condition_link.js @@ -15,20 +15,21 @@ import React, { Component } from 'react'; import { EuiFieldNumber, EuiFlexGroup, EuiFlexItem, EuiLink, EuiText } from '@elastic/eui'; -import { APPLIES_TO } from '../../../../../common/constants/detector_rule'; -import { formatValue } from '../../../formatters/format_value'; -import { getAppliesToValueFromAnomaly } from '../utils'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { ML_DETECTOR_RULE_APPLIES_TO } from '@kbn/ml-anomaly-utils'; + +import { formatValue } from '../../../formatters/format_value'; +import { getAppliesToValueFromAnomaly } from '../utils'; export class EditConditionLink extends Component { static propTypes = { conditionIndex: PropTypes.number.isRequired, conditionValue: PropTypes.number.isRequired, appliesTo: PropTypes.oneOf([ - APPLIES_TO.ACTUAL, - APPLIES_TO.TYPICAL, - APPLIES_TO.DIFF_FROM_TYPICAL, + ML_DETECTOR_RULE_APPLIES_TO.ACTUAL, + ML_DETECTOR_RULE_APPLIES_TO.TYPICAL, + ML_DETECTOR_RULE_APPLIES_TO.DIFF_FROM_TYPICAL, ]), anomaly: PropTypes.object.isRequired, updateConditionValue: PropTypes.func.isRequired, diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/select_rule_action/edit_condition_link.test.js b/x-pack/plugins/ml/public/application/components/rule_editor/select_rule_action/edit_condition_link.test.js index d8f8f4d5d8f51..ddcffadcacbb3 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/select_rule_action/edit_condition_link.test.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/select_rule_action/edit_condition_link.test.js @@ -7,11 +7,12 @@ jest.mock('../../../services/job_service', () => 'mlJobService'); -import { shallowWithIntl } from '@kbn/test-jest-helpers'; import React from 'react'; +import { shallowWithIntl } from '@kbn/test-jest-helpers'; +import { ML_DETECTOR_RULE_APPLIES_TO } from '@kbn/ml-anomaly-utils'; + import { EditConditionLink } from './edit_condition_link'; -import { APPLIES_TO } from '../../../../../common/constants/detector_rule'; function prepareTest(updateConditionValueFn, appliesTo) { const anomaly = { @@ -41,22 +42,25 @@ describe('EditConditionLink', () => { const updateConditionValue = jest.fn(() => {}); test(`renders for a condition using actual`, () => { - const wrapper = prepareTest(updateConditionValue, APPLIES_TO.ACTUAL); + const wrapper = prepareTest(updateConditionValue, ML_DETECTOR_RULE_APPLIES_TO.ACTUAL); expect(wrapper).toMatchSnapshot(); }); test(`renders for a condition using typical`, () => { - const wrapper = prepareTest(updateConditionValue, APPLIES_TO.TYPICAL); + const wrapper = prepareTest(updateConditionValue, ML_DETECTOR_RULE_APPLIES_TO.TYPICAL); expect(wrapper).toMatchSnapshot(); }); test(`renders for a condition using diff from typical`, () => { - const wrapper = prepareTest(updateConditionValue, APPLIES_TO.DIFF_FROM_TYPICAL); + const wrapper = prepareTest( + updateConditionValue, + ML_DETECTOR_RULE_APPLIES_TO.DIFF_FROM_TYPICAL + ); expect(wrapper).toMatchSnapshot(); }); test('calls updateConditionValue on clicking update link', () => { - const wrapper = prepareTest(updateConditionValue, APPLIES_TO.ACTUAL); + const wrapper = prepareTest(updateConditionValue, ML_DETECTOR_RULE_APPLIES_TO.ACTUAL); const instance = wrapper.instance(); instance.onUpdateClick(); wrapper.update(); diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/select_rule_action/rule_action_panel.test.js b/x-pack/plugins/ml/public/application/components/rule_editor/select_rule_action/rule_action_panel.test.js index 6f019d973cc8d..6d5b2e38346d3 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/select_rule_action/rule_action_panel.test.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/select_rule_action/rule_action_panel.test.js @@ -29,11 +29,12 @@ jest.mock('../../../services/ml_api_service', () => ({ }, })); -import { shallowWithIntl } from '@kbn/test-jest-helpers'; import React from 'react'; +import { shallowWithIntl } from '@kbn/test-jest-helpers'; +import { ML_DETECTOR_RULE_ACTION } from '@kbn/ml-anomaly-utils'; + import { RuleActionPanel } from './rule_action_panel'; -import { ACTION } from '../../../../../common/constants/detector_rule'; describe('RuleActionPanel', () => { const job = { @@ -44,7 +45,7 @@ describe('RuleActionPanel', () => { detector_description: 'mean response time', custom_rules: [ { - actions: [ACTION.SKIP_RESULT], + actions: [ML_DETECTOR_RULE_ACTION.SKIP_RESULT], conditions: [ { applies_to: 'actual', @@ -54,7 +55,7 @@ describe('RuleActionPanel', () => { ], }, { - actions: [ACTION.SKIP_MODEL_UPDATE], + actions: [ML_DETECTOR_RULE_ACTION.SKIP_MODEL_UPDATE], scope: { airline: { filter_id: 'eu-airlines', @@ -63,7 +64,7 @@ describe('RuleActionPanel', () => { }, }, { - actions: [ACTION.SKIP_MODEL_UPDATE], + actions: [ML_DETECTOR_RULE_ACTION.SKIP_MODEL_UPDATE], scope: { airline: { filter_id: 'eu-airlines', diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/utils.js b/x-pack/plugins/ml/public/application/components/rule_editor/utils.js index e4e5940873255..fd51ccd7b6b3c 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/utils.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/utils.js @@ -5,37 +5,38 @@ * 2.0. */ +import { cloneDeep } from 'lodash'; + +import { i18n } from '@kbn/i18n'; import { - ACTION, - APPLIES_TO, - FILTER_TYPE, - OPERATOR, -} from '../../../../common/constants/detector_rule'; + ML_DETECTOR_RULE_ACTION, + ML_DETECTOR_RULE_APPLIES_TO, + ML_DETECTOR_RULE_FILTER_TYPE, + ML_DETECTOR_RULE_OPERATOR, +} from '@kbn/ml-anomaly-utils'; -import { cloneDeep } from 'lodash'; import { ml } from '../../services/ml_api_service'; import { mlJobService } from '../../services/job_service'; -import { i18n } from '@kbn/i18n'; import { processCreatedBy } from '../../../../common/util/job_utils'; export function getNewConditionDefaults() { return { - applies_to: APPLIES_TO.ACTUAL, - operator: OPERATOR.LESS_THAN, + applies_to: ML_DETECTOR_RULE_APPLIES_TO.ACTUAL, + operator: ML_DETECTOR_RULE_OPERATOR.LESS_THAN, value: 1, }; } export function getNewRuleDefaults() { return { - actions: [ACTION.SKIP_RESULT], + actions: [ML_DETECTOR_RULE_ACTION.SKIP_RESULT], conditions: [], }; } export function getScopeFieldDefaults(filterListIds) { const defaults = { - filter_type: FILTER_TYPE.INCLUDE, + filter_type: ML_DETECTOR_RULE_FILTER_TYPE.INCLUDE, enabled: false, // UI-only property to show field as enabled in Scope section. }; @@ -191,7 +192,7 @@ export function buildRuleDescription(rule) { actionsText += ' AND '; } switch (action) { - case ACTION.SKIP_RESULT: + case ML_DETECTOR_RULE_ACTION.SKIP_RESULT: actionsText += i18n.translate('xpack.ml.ruleEditor.ruleDescription.resultActionTypeText', { defaultMessage: 'result', description: @@ -199,7 +200,7 @@ export function buildRuleDescription(rule) { 'xpack.ml.ruleEditor.ruleDescription.conditionsText + xpack.ml.ruleEditor.ruleDescription.filtersText', }); break; - case ACTION.SKIP_MODEL_UPDATE: + case ML_DETECTOR_RULE_ACTION.SKIP_MODEL_UPDATE: actionsText += i18n.translate( 'xpack.ml.ruleEditor.ruleDescription.modelUpdateActionTypeText', { @@ -273,9 +274,9 @@ export function buildRuleDescription(rule) { export function filterTypeToText(filterType) { switch (filterType) { - case FILTER_TYPE.INCLUDE: + case ML_DETECTOR_RULE_FILTER_TYPE.INCLUDE: return i18n.translate('xpack.ml.ruleEditor.includeFilterTypeText', { defaultMessage: 'in' }); - case FILTER_TYPE.EXCLUDE: + case ML_DETECTOR_RULE_FILTER_TYPE.EXCLUDE: return i18n.translate('xpack.ml.ruleEditor.excludeFilterTypeText', { defaultMessage: 'not in', }); @@ -287,16 +288,16 @@ export function filterTypeToText(filterType) { export function appliesToText(appliesTo) { switch (appliesTo) { - case APPLIES_TO.ACTUAL: + case ML_DETECTOR_RULE_APPLIES_TO.ACTUAL: return i18n.translate('xpack.ml.ruleEditor.actualAppliesTypeText', { defaultMessage: 'actual', }); - case APPLIES_TO.TYPICAL: + case ML_DETECTOR_RULE_APPLIES_TO.TYPICAL: return i18n.translate('xpack.ml.ruleEditor.typicalAppliesTypeText', { defaultMessage: 'typical', }); - case APPLIES_TO.DIFF_FROM_TYPICAL: + case ML_DETECTOR_RULE_APPLIES_TO.DIFF_FROM_TYPICAL: return i18n.translate('xpack.ml.ruleEditor.diffFromTypicalAppliesTypeText', { defaultMessage: 'diff from typical', }); @@ -308,22 +309,22 @@ export function appliesToText(appliesTo) { export function operatorToText(operator) { switch (operator) { - case OPERATOR.LESS_THAN: + case ML_DETECTOR_RULE_OPERATOR.LESS_THAN: return i18n.translate('xpack.ml.ruleEditor.lessThanOperatorTypeText', { defaultMessage: 'less than', }); - case OPERATOR.LESS_THAN_OR_EQUAL: + case ML_DETECTOR_RULE_OPERATOR.LESS_THAN_OR_EQUAL: return i18n.translate('xpack.ml.ruleEditor.lessThanOrEqualToOperatorTypeText', { defaultMessage: 'less than or equal to', }); - case OPERATOR.GREATER_THAN: + case ML_DETECTOR_RULE_OPERATOR.GREATER_THAN: return i18n.translate('xpack.ml.ruleEditor.greaterThanOperatorTypeText', { defaultMessage: 'greater than', }); - case OPERATOR.GREATER_THAN_OR_EQUAL: + case ML_DETECTOR_RULE_OPERATOR.GREATER_THAN_OR_EQUAL: return i18n.translate('xpack.ml.ruleEditor.greaterThanOrEqualToOperatorTypeText', { defaultMessage: 'greater than or equal to', }); @@ -350,13 +351,13 @@ export function getAppliesToValueFromAnomaly(anomaly, appliesTo) { } switch (appliesTo) { - case APPLIES_TO.ACTUAL: + case ML_DETECTOR_RULE_APPLIES_TO.ACTUAL: return actualValue; - case APPLIES_TO.TYPICAL: + case ML_DETECTOR_RULE_APPLIES_TO.TYPICAL: return typicalValue; - case APPLIES_TO.DIFF_FROM_TYPICAL: + case ML_DETECTOR_RULE_APPLIES_TO.DIFF_FROM_TYPICAL: if (actual !== undefined && typical !== undefined) { return Math.abs(actualValue - typicalValue); } diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/utils.test.js b/x-pack/plugins/ml/public/application/components/rule_editor/utils.test.js index 9fd375376c161..62a6895ce1006 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/utils.test.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/utils.test.js @@ -5,50 +5,51 @@ * 2.0. */ -import { isValidRule, buildRuleDescription, getAppliesToValueFromAnomaly } from './utils'; import { - ACTION, - APPLIES_TO, - OPERATOR, - FILTER_TYPE, -} from '../../../../common/constants/detector_rule'; + ML_DETECTOR_RULE_ACTION, + ML_DETECTOR_RULE_APPLIES_TO, + ML_DETECTOR_RULE_OPERATOR, + ML_DETECTOR_RULE_FILTER_TYPE, +} from '@kbn/ml-anomaly-utils'; + +import { isValidRule, buildRuleDescription, getAppliesToValueFromAnomaly } from './utils'; describe('ML - rule editor utils', () => { const ruleWithCondition = { - actions: [ACTION.SKIP_RESULT], + actions: [ML_DETECTOR_RULE_ACTION.SKIP_RESULT], conditions: [ { - applies_to: APPLIES_TO.ACTUAL, - operator: OPERATOR.GREATER_THAN, + applies_to: ML_DETECTOR_RULE_APPLIES_TO.ACTUAL, + operator: ML_DETECTOR_RULE_OPERATOR.GREATER_THAN, value: 10, }, ], }; const ruleWithScope = { - actions: [ACTION.SKIP_RESULT], + actions: [ML_DETECTOR_RULE_ACTION.SKIP_RESULT], scope: { instance: { filter_id: 'test_aws_instances', - filter_type: FILTER_TYPE.INCLUDE, + filter_type: ML_DETECTOR_RULE_FILTER_TYPE.INCLUDE, enabled: true, }, }, }; const ruleWithConditionAndScope = { - actions: [ACTION.SKIP_RESULT], + actions: [ML_DETECTOR_RULE_ACTION.SKIP_RESULT], conditions: [ { - applies_to: APPLIES_TO.TYPICAL, - operator: OPERATOR.LESS_THAN, + applies_to: ML_DETECTOR_RULE_APPLIES_TO.TYPICAL, + operator: ML_DETECTOR_RULE_OPERATOR.LESS_THAN, value: 100, }, ], scope: { instance: { filter_id: 'test_aws_instances', - filter_type: FILTER_TYPE.EXCLUDE, + filter_type: ML_DETECTOR_RULE_FILTER_TYPE.EXCLUDE, enabled: true, }, }, @@ -72,8 +73,8 @@ describe('ML - rule editor utils', () => { actions: [], conditions: [ { - applies_to: APPLIES_TO.TYPICAL, - operator: OPERATOR.LESS_THAN, + applies_to: ML_DETECTOR_RULE_APPLIES_TO.TYPICAL, + operator: ML_DETECTOR_RULE_OPERATOR.LESS_THAN, value: 100, }, ], @@ -84,7 +85,7 @@ describe('ML - rule editor utils', () => { test('returns false for a rule with no scope or conditions', () => { const ruleWithNoScopeOrCondition = { - actions: [ACTION.SKIP_RESULT], + actions: [ML_DETECTOR_RULE_ACTION.SKIP_RESULT], }; expect(isValidRule(ruleWithNoScopeOrCondition)).toBe(false); @@ -112,15 +113,17 @@ describe('ML - rule editor utils', () => { }; test('returns expected actual value from an anomaly', () => { - expect(getAppliesToValueFromAnomaly(anomaly, APPLIES_TO.ACTUAL)).toBe(210); + expect(getAppliesToValueFromAnomaly(anomaly, ML_DETECTOR_RULE_APPLIES_TO.ACTUAL)).toBe(210); }); test('returns expected typical value from an anomaly', () => { - expect(getAppliesToValueFromAnomaly(anomaly, APPLIES_TO.TYPICAL)).toBe(1.23); + expect(getAppliesToValueFromAnomaly(anomaly, ML_DETECTOR_RULE_APPLIES_TO.TYPICAL)).toBe(1.23); }); test('returns expected diff from typical value from an anomaly', () => { - expect(getAppliesToValueFromAnomaly(anomaly, APPLIES_TO.DIFF_FROM_TYPICAL)).toBe(208.77); + expect( + getAppliesToValueFromAnomaly(anomaly, ML_DETECTOR_RULE_APPLIES_TO.DIFF_FROM_TYPICAL) + ).toBe(208.77); }); }); }); diff --git a/x-pack/plugins/ml/public/application/components/severity_control/severity_control.tsx b/x-pack/plugins/ml/public/application/components/severity_control/severity_control.tsx index 1ac6fc5ff46de..7c5424446967a 100644 --- a/x-pack/plugins/ml/public/application/components/severity_control/severity_control.tsx +++ b/x-pack/plugins/ml/public/application/components/severity_control/severity_control.tsx @@ -15,7 +15,7 @@ import { EuiRange, EuiRangeProps, } from '@elastic/eui'; -import { ANOMALY_THRESHOLD } from '../../../../common'; +import { ML_ANOMALY_THRESHOLD } from '@kbn/ml-anomaly-utils'; export interface SeveritySelectorProps { value: number | undefined; @@ -27,22 +27,22 @@ const MAX_ANOMALY_SCORE = 100; export const SeverityControl: FC = React.memo(({ value, onChange }) => { const levels: EuiRangeProps['levels'] = [ { - min: ANOMALY_THRESHOLD.LOW, - max: ANOMALY_THRESHOLD.MINOR, + min: ML_ANOMALY_THRESHOLD.LOW, + max: ML_ANOMALY_THRESHOLD.MINOR, color: '#8BC8FB', }, { - min: ANOMALY_THRESHOLD.MINOR, - max: ANOMALY_THRESHOLD.MAJOR, + min: ML_ANOMALY_THRESHOLD.MINOR, + max: ML_ANOMALY_THRESHOLD.MAJOR, color: '#FDEC25', }, { - min: ANOMALY_THRESHOLD.MAJOR, - max: ANOMALY_THRESHOLD.CRITICAL, + min: ML_ANOMALY_THRESHOLD.MAJOR, + max: ML_ANOMALY_THRESHOLD.CRITICAL, color: '#FBA740', }, { - min: ANOMALY_THRESHOLD.CRITICAL, + min: ML_ANOMALY_THRESHOLD.CRITICAL, max: MAX_ANOMALY_SCORE, color: '#FE5050', }, @@ -52,7 +52,7 @@ export const SeverityControl: FC = React.memo(({ value, o defaultMessage: 'Severity', }); - const resultValue = value ?? ANOMALY_THRESHOLD.LOW; + const resultValue = value ?? ML_ANOMALY_THRESHOLD.LOW; const ticks = new Array(5).fill(null).map((x, i) => { const v = i * 25; @@ -70,7 +70,7 @@ export const SeverityControl: FC = React.memo(({ value, o prepend={label} value={resultValue} onChange={(e) => onChange(Number(e.target.value))} - min={ANOMALY_THRESHOLD.LOW} + min={ML_ANOMALY_THRESHOLD.LOW} max={MAX_ANOMALY_SCORE} /> @@ -78,7 +78,7 @@ export const SeverityControl: FC = React.memo(({ value, o onChange(Number(e.currentTarget.value))} diff --git a/x-pack/plugins/ml/public/application/components/technical_preview_badge/technical_preview_badge.tsx b/x-pack/plugins/ml/public/application/components/technical_preview_badge/technical_preview_badge.tsx index a216a7ce7fc75..95f1a0d54b10a 100644 --- a/x-pack/plugins/ml/public/application/components/technical_preview_badge/technical_preview_badge.tsx +++ b/x-pack/plugins/ml/public/application/components/technical_preview_badge/technical_preview_badge.tsx @@ -11,13 +11,13 @@ import { EuiBetaBadge } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -export const TechnicalPreviewBadge: FC = () => { +export const TechnicalPreviewBadge: FC<{ compressed?: boolean }> = ({ compressed = false }) => { return ( = ({ [indexData?.visibleColumns, discoverLocator, dataViewId, resultsField, tableItems, data] ); - const openCustomUrl = (item: DataGridItem, customUrl: KibanaUrlConfig) => { + const openCustomUrl = (item: DataGridItem, customUrl: MlKibanaUrlConfig) => { // Replace any tokens in the configured url_value with values from the source record and open link in a new tab/window. const urlPath = replaceTokensInDFAUrlValue( customUrl, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_edit/edit_action_flyout.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_edit/edit_action_flyout.tsx index 4730dea72dd7a..8855c674e34c6 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_edit/edit_action_flyout.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_edit/edit_action_flyout.tsx @@ -28,6 +28,8 @@ import { EuiTitle, } from '@elastic/eui'; +import type { MlUrlConfig } from '@kbn/ml-anomaly-utils'; + import { useMlKibana, useMlApiContext } from '../../../../../contexts/kibana'; import { ml } from '../../../../../services/ml_api_service'; import { useToastNotificationService } from '../../../../../services/toast_notification_service'; @@ -41,7 +43,6 @@ import { UpdateDataFrameAnalyticsConfig, type DataFrameAnalyticsConfig, } from '../../../../../../../common/types/data_frame_analytics'; -import type { UrlConfig } from '../../../../../../../common/types/custom_urls'; import { EditAction } from './use_edit_action'; import { CustomUrlsWrapper, isValidCustomUrls } from '../../../../../components/custom_urls'; @@ -62,7 +63,7 @@ export const EditActionFlyout: FC> = ({ closeFlyout, item } const [mmlValidationError, setMmlValidationError] = useState(); const [maxNumThreads, setMaxNumThreads] = useState(config.max_num_threads); const [activeTabId, setActiveTabId] = useState('job-details'); - const [customUrls, setCustomUrls] = useState([]); + const [customUrls, setCustomUrls] = useState([]); const [analyticsJob, setAnalyticsJob] = useState(); const { diff --git a/x-pack/plugins/ml/public/application/explorer/anomalies_map.tsx b/x-pack/plugins/ml/public/application/explorer/anomalies_map.tsx index 3edfcd9842a18..c6ab5e52f5ef7 100644 --- a/x-pack/plugins/ml/public/application/explorer/anomalies_map.tsx +++ b/x-pack/plugins/ml/public/application/explorer/anomalies_map.tsx @@ -26,13 +26,13 @@ import { } from '@kbn/maps-plugin/common'; import { EMSTermJoinConfig } from '@kbn/maps-plugin/public'; import { isDefined } from '@kbn/ml-is-defined'; +import type { MlAnomaliesTableRecord } from '@kbn/ml-anomaly-utils'; import { useMlKibana } from '../contexts/kibana'; import { MlEmbeddedMapComponent } from '../components/ml_embedded_map'; -import { AnomaliesTableRecord } from '../../../common/types/anomalies'; const MAX_ENTITY_VALUES = 3; -function getAnomalyRows(anomalies: AnomaliesTableRecord[], jobId: string) { +function getAnomalyRows(anomalies: MlAnomaliesTableRecord[], jobId: string) { const anomalyRows: Record = {}; for (let i = 0; i < anomalies.length; i++) { @@ -58,7 +58,7 @@ function getAnomalyRows(anomalies: AnomaliesTableRecord[], jobId: string) { } export const getChoroplethAnomaliesLayer = ( - anomalies: AnomaliesTableRecord[], + anomalies: MlAnomaliesTableRecord[], { layerId, field, jobId }: MLEMSTermJoinConfig ): VectorLayerDescriptor => { return { @@ -131,7 +131,7 @@ export const getChoroplethAnomaliesLayer = ( }; interface Props { - anomalies: AnomaliesTableRecord[]; + anomalies: MlAnomaliesTableRecord[]; jobIds: string[]; } diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/components/explorer_chart_label/entity_filter/entity_filter.tsx b/x-pack/plugins/ml/public/application/explorer/explorer_charts/components/explorer_chart_label/entity_filter/entity_filter.tsx index 15822d74d08d5..1ac6644a14d4e 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/components/explorer_chart_label/entity_filter/entity_filter.tsx +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/components/explorer_chart_label/entity_filter/entity_filter.tsx @@ -8,10 +8,7 @@ import React, { FC } from 'react'; import { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import { - ENTITY_FIELD_OPERATIONS, - EntityFieldOperation, -} from '../../../../../../../common/util/anomaly_utils'; +import { ML_ENTITY_FIELD_OPERATIONS, MlEntityFieldOperation } from '@kbn/ml-anomaly-utils'; import { blurButtonOnClick } from '../../../../../util/component_utils'; import './_entity_filter.scss'; @@ -19,7 +16,7 @@ interface EntityFilterProps { onFilter: (params: { influencerFieldName: string; influencerFieldValue: string; - action: EntityFieldOperation; + action: MlEntityFieldOperation; }) => void; influencerFieldName: string; influencerFieldValue: string; @@ -46,7 +43,7 @@ export const EntityFilter: FC = ({ onFilter({ influencerFieldName, influencerFieldValue, - action: ENTITY_FIELD_OPERATIONS.ADD, + action: ML_ENTITY_FIELD_OPERATIONS.ADD, }); })} iconType="plusInCircle" @@ -71,7 +68,7 @@ export const EntityFilter: FC = ({ onFilter({ influencerFieldName, influencerFieldValue, - action: ENTITY_FIELD_OPERATIONS.REMOVE, + action: ML_ENTITY_FIELD_OPERATIONS.REMOVE, }); })} iconType="minusInCircle" diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_anomalies_container.tsx b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_anomalies_container.tsx index 3d58a7a571c4a..f2f59f220d7d7 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_anomalies_container.tsx +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_anomalies_container.tsx @@ -11,13 +11,13 @@ import React, { FC } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import type { TimefilterContract } from '@kbn/data-plugin/public'; import { ChartsPluginStart } from '@kbn/charts-plugin/public'; +import type { MlEntityFieldOperation } from '@kbn/ml-anomaly-utils'; import { ExplorerChartsContainer } from './explorer_charts_container'; import { SelectSeverityUI, TableSeverity, } from '../../components/controls/select_severity/select_severity'; import type { TimeBuckets } from '../../util/time_buckets'; -import type { EntityFieldOperation } from '../../../../common/util/anomaly_utils'; import type { ExplorerChartsData } from './explorer_charts_container_service'; import type { MlLocator } from '../../../../common/types/locator'; @@ -30,7 +30,11 @@ interface ExplorerAnomaliesContainerProps { mlLocator: MlLocator; timeBuckets: TimeBuckets; timefilter: TimefilterContract; - onSelectEntity: (fieldName: string, fieldValue: string, operation: EntityFieldOperation) => void; + onSelectEntity: ( + fieldName: string, + fieldValue: string, + operation: MlEntityFieldOperation + ) => void; showSelectedInterval?: boolean; chartsService: ChartsPluginStart; timeRange: { from: string; to: string } | undefined; diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_config_builder.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_config_builder.js index 7d723995e3ac4..c97e73960dcdd 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_config_builder.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_config_builder.js @@ -11,7 +11,7 @@ */ import { parseInterval } from '../../../../common/util/parse_interval'; -import { getEntityFieldList } from '../../../../common/util/anomaly_utils'; +import { getEntityFieldList } from '@kbn/ml-anomaly-utils'; import { buildConfigFromDetector } from '../../util/chart_config_builder'; import { mlJobService } from '../../services/job_service'; import { mlFunctionToESAggregation } from '../../../../common/util/job_utils'; diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js index 204a4f3677579..8242130f993f5 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js @@ -24,7 +24,7 @@ import { getFormattedSeverityScore, getSeverityColor, getSeverityWithLow, -} from '../../../../common/util/anomaly_utils'; +} from '@kbn/ml-anomaly-utils'; import { getChartType, getTickValues, diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js index 3f367d189762d..8fe832cb6afc8 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js @@ -24,7 +24,7 @@ import { getFormattedSeverityScore, getSeverityColor, getSeverityWithLow, -} from '../../../../common/util/anomaly_utils'; +} from '@kbn/ml-anomaly-utils'; import { LINE_CHART_ANOMALY_RADIUS, MULTI_BUCKET_SYMBOL_SIZE, diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/map_config.ts b/x-pack/plugins/ml/public/application/explorer/explorer_charts/map_config.ts index 6ab5a04fdde65..d5b57075a16e2 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/map_config.ts +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/map_config.ts @@ -6,7 +6,7 @@ */ import { FIELD_ORIGIN, LAYER_TYPE, STYLE_TYPE } from '@kbn/maps-plugin/common'; -import { SEVERITY_COLOR_RAMP } from '../../../../common'; +import { ML_SEVERITY_COLOR_RAMP } from '@kbn/ml-anomaly-utils'; import { AnomaliesTableData } from '../explorer_utils'; const FEATURE = 'Feature'; @@ -115,7 +115,7 @@ export const getMLAnomaliesActualLayer = (anomalies: any) => { fillColor: { type: STYLE_TYPE.DYNAMIC, options: { - customColorRamp: SEVERITY_COLOR_RAMP, + customColorRamp: ML_SEVERITY_COLOR_RAMP, field: { name: 'record_score', origin: FIELD_ORIGIN.SOURCE, diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_utils.ts b/x-pack/plugins/ml/public/application/explorer/explorer_utils.ts index f0f26a3918438..f6744dbd272b0 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_utils.ts +++ b/x-pack/plugins/ml/public/application/explorer/explorer_utils.ts @@ -18,12 +18,17 @@ import { asyncForEach } from '@kbn/std'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import type { DataViewsContract } from '@kbn/data-views-plugin/public'; import { extractErrorMessage } from '@kbn/ml-error-utils'; +import { + getEntityFieldList, + type MlEntityField, + type MlInfluencer, + type MlRecordForInfluencer, +} from '@kbn/ml-anomaly-utils'; import { ANNOTATIONS_TABLE_DEFAULT_QUERY_SIZE, ANOMALIES_TABLE_DEFAULT_QUERY_SIZE, } from '../../../common/constants/search'; -import { EntityField, getEntityFieldList } from '../../../common/util/anomaly_utils'; import { getDataViewIdFromName } from '../util/index_utils'; import { ML_JOB_AGGREGATION } from '../../../common/constants/aggregation_types'; import { @@ -49,8 +54,6 @@ import { MlResultsService } from '../services/results_service'; import { InfluencersFilterQuery } from '../../../common/types/es_client'; import { TimeRangeBounds } from '../util/time_buckets'; import { Annotations, AnnotationsTable } from '../../../common/types/annotations'; -import { Influencer } from '../../../common/types/anomalies'; -import { RecordForInfluencer } from '../services/results_service/results_service'; export interface ExplorerJob { id: string; @@ -108,7 +111,7 @@ export interface AnomaliesTableData { jobIds: string[]; } -export interface ChartRecord extends RecordForInfluencer { +export interface ChartRecord extends MlRecordForInfluencer { function: string; } @@ -191,7 +194,7 @@ export async function loadFilteredTopInfluencers( // Add the influencers from the top scoring anomalies. records.forEach((record) => { - const influencersByName: Influencer[] = record.influencers || []; + const influencersByName: MlInfluencer[] = record.influencers || []; influencersByName.forEach((influencer) => { const fieldName = influencer.influencer_field_name; const fieldValues = influencer.influencer_field_values; @@ -208,7 +211,7 @@ export async function loadFilteredTopInfluencers( uniqValuesByName[fieldName] = uniq(fieldValues); }); - const filterInfluencers: EntityField[] = []; + const filterInfluencers: MlEntityField[] = []; Object.keys(uniqValuesByName).forEach((fieldName) => { // Find record influencers with the same field name as the clicked on cell(s). const matchingFieldName = influencers.find((influencer) => { @@ -321,7 +324,7 @@ export function getSelectionTimeRange( export function getSelectionInfluencers( selectedCells: AppStateSelectedCells | undefined | null, fieldName: string -): EntityField[] { +): MlEntityField[] { if ( !!selectedCells && selectedCells.type !== SWIMLANE_TYPE.OVERALL && diff --git a/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx b/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx index a34c968eecfe4..cbff2587ccf4c 100644 --- a/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx +++ b/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx @@ -36,9 +36,13 @@ import moment from 'moment'; import { i18n } from '@kbn/i18n'; import { ChartsPluginStart, useActiveCursor } from '@kbn/charts-plugin/public'; import { css } from '@emotion/react'; +import { + getFormattedSeverityScore, + ML_ANOMALY_THRESHOLD, + ML_SEVERITY_COLORS, +} from '@kbn/ml-anomaly-utils'; import { SwimLanePagination } from './swimlane_pagination'; import { AppStateSelectedCells, OverallSwimlaneData, ViewBySwimLaneData } from './explorer_utils'; -import { ANOMALY_THRESHOLD, SEVERITY_COLORS } from '../../../common'; import { TimeBuckets as TimeBucketsClass } from '../util/time_buckets'; import { SWIMLANE_TYPE, SwimlaneType } from './explorer_constants'; import { mlEscape } from '../util/string_utils'; @@ -60,9 +64,6 @@ declare global { } } -function getFormattedSeverityScore(score: number): string { - return String(parseInt(String(score), 10)); -} /** * Ignore insignificant resize, e.g. browser scrollbar appearance. */ @@ -456,29 +457,29 @@ export const SwimlaneContainer: FC = ({ type: 'bands', bands: [ { - start: ANOMALY_THRESHOLD.LOW, - end: ANOMALY_THRESHOLD.WARNING, - color: SEVERITY_COLORS.LOW, + start: ML_ANOMALY_THRESHOLD.LOW, + end: ML_ANOMALY_THRESHOLD.WARNING, + color: ML_SEVERITY_COLORS.LOW, }, { - start: ANOMALY_THRESHOLD.WARNING, - end: ANOMALY_THRESHOLD.MINOR, - color: SEVERITY_COLORS.WARNING, + start: ML_ANOMALY_THRESHOLD.WARNING, + end: ML_ANOMALY_THRESHOLD.MINOR, + color: ML_SEVERITY_COLORS.WARNING, }, { - start: ANOMALY_THRESHOLD.MINOR, - end: ANOMALY_THRESHOLD.MAJOR, - color: SEVERITY_COLORS.MINOR, + start: ML_ANOMALY_THRESHOLD.MINOR, + end: ML_ANOMALY_THRESHOLD.MAJOR, + color: ML_SEVERITY_COLORS.MINOR, }, { - start: ANOMALY_THRESHOLD.MAJOR, - end: ANOMALY_THRESHOLD.CRITICAL, - color: SEVERITY_COLORS.MAJOR, + start: ML_ANOMALY_THRESHOLD.MAJOR, + end: ML_ANOMALY_THRESHOLD.CRITICAL, + color: ML_SEVERITY_COLORS.MAJOR, }, { - start: ANOMALY_THRESHOLD.CRITICAL, + start: ML_ANOMALY_THRESHOLD.CRITICAL, end: Infinity, - color: SEVERITY_COLORS.CRITICAL, + color: ML_SEVERITY_COLORS.CRITICAL, }, ], }} diff --git a/x-pack/plugins/ml/public/application/formatters/format_value.test.ts b/x-pack/plugins/ml/public/application/formatters/format_value.test.ts index 57f78c1267765..b91e1c7934c9d 100644 --- a/x-pack/plugins/ml/public/application/formatters/format_value.test.ts +++ b/x-pack/plugins/ml/public/application/formatters/format_value.test.ts @@ -6,11 +6,11 @@ */ import moment from 'moment-timezone'; -import { AnomalyRecordDoc } from '../../../common/types/anomalies'; +import { MlAnomalyRecordDoc } from '@kbn/ml-anomaly-utils'; import { formatValue } from './format_value'; describe('ML - formatValue formatter', () => { - const timeOfWeekRecord: AnomalyRecordDoc = { + const timeOfWeekRecord: MlAnomalyRecordDoc = { job_id: 'gallery_time_of_week', result_type: 'record', probability: 0.012818, @@ -27,7 +27,7 @@ describe('ML - formatValue formatter', () => { function_description: 'time', }; - const timeOfDayRecord: AnomalyRecordDoc = { + const timeOfDayRecord: MlAnomalyRecordDoc = { job_id: 'gallery_time_of_day', result_type: 'record', probability: 0.012818, diff --git a/x-pack/plugins/ml/public/application/formatters/format_value.ts b/x-pack/plugins/ml/public/application/formatters/format_value.ts index f08e6382120a4..5af8b1bd40e43 100644 --- a/x-pack/plugins/ml/public/application/formatters/format_value.ts +++ b/x-pack/plugins/ml/public/application/formatters/format_value.ts @@ -13,7 +13,8 @@ */ import moment from 'moment'; -import { AnomalyRecordDoc } from '../../../common/types/anomalies'; +import { MlAnomalyRecordDoc } from '@kbn/ml-anomaly-utils'; + const SIGFIGS_IF_ROUNDING = 3; // Number of sigfigs to use for values < 10 // Formats the value of an actual or typical field from a machine learning anomaly record. @@ -28,7 +29,7 @@ export function formatValue( value: number[] | number, mlFunction: string, fieldFormat?: any, - record?: AnomalyRecordDoc + record?: MlAnomalyRecordDoc ) { // actual and typical values in anomaly record results will be arrays. // Unless the array is multi-valued (as it will be for multi-variate analyses such as lat_long), @@ -58,7 +59,7 @@ export function formatSingleValue( value: number, mlFunction?: string, fieldFormat?: any, - record?: AnomalyRecordDoc + record?: MlAnomalyRecordDoc ) { if (value === undefined || value === null) { return ''; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts index 2d28256336757..d1c0364791064 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts @@ -11,8 +11,8 @@ import { ES_FIELD_TYPES } from '@kbn/field-types'; import type { Query } from '@kbn/es-query'; import type { DataView } from '@kbn/data-views-plugin/public'; import { addExcludeFrozenToQuery } from '@kbn/ml-query-utils'; +import { MlUrlConfig } from '@kbn/ml-anomaly-utils'; import { SavedSearchSavedObject } from '../../../../../../common/types/kibana'; -import { UrlConfig } from '../../../../../../common/types/custom_urls'; import { IndexPatternTitle } from '../../../../../../common/types/kibana'; import { ML_JOB_AGGREGATION, @@ -717,12 +717,12 @@ export class JobCreator { return this._getCustomSetting('created_by') as CREATED_BY_LABEL | null; } - public set customUrls(customUrls: UrlConfig[] | null) { + public set customUrls(customUrls: MlUrlConfig[] | null) { this._setCustomSetting('custom_urls', customUrls); } - public get customUrls(): UrlConfig[] | null { - return this._getCustomSetting('custom_urls') as UrlConfig[] | null; + public get customUrls(): MlUrlConfig[] | null { + return this._getCustomSetting('custom_urls') as MlUrlConfig[] | null; } public get formattedJobJson() { diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/results_loader/results_loader.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/results_loader/results_loader.ts index 772a385ed60a0..e36f02a96eda2 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/results_loader/results_loader.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/results_loader/results_loader.ts @@ -6,17 +6,21 @@ */ import { BehaviorSubject, lastValueFrom } from 'rxjs'; -import { JobCreatorType, isMultiMetricJobCreator } from '../job_creator'; -import { mlResultsService, ModelPlotOutputResults } from '../../../../services/results_service'; -import { TimeBuckets } from '../../../../util/time_buckets'; -import { getSeverityType } from '../../../../../../common/util/anomaly_utils'; + +import { getSeverityType, ML_ANOMALY_SEVERITY } from '@kbn/ml-anomaly-utils'; + import { parseInterval } from '../../../../../../common/util/parse_interval'; -import { ANOMALY_SEVERITY } from '../../../../../../common/constants/anomalies'; -import { getScoresByRecord } from './searches'; -import { ChartLoader } from '../chart_loader'; import { JOB_TYPE } from '../../../../../../common/constants/new_job'; import { ES_AGGREGATION } from '../../../../../../common/constants/aggregation_types'; +import { mlResultsService, ModelPlotOutputResults } from '../../../../services/results_service'; +import { TimeBuckets } from '../../../../util/time_buckets'; + +import { JobCreatorType, isMultiMetricJobCreator } from '../job_creator'; +import { ChartLoader } from '../chart_loader'; + +import { getScoresByRecord } from './searches'; + export interface Results { progress: number; model: Record; @@ -33,7 +37,7 @@ export interface ModelItem { export interface Anomaly { time: number; value: number; - severity: ANOMALY_SEVERITY; + severity: ML_ANOMALY_SEVERITY; } const emptyModelItem = { diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/charts/common/anomalies.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/charts/common/anomalies.tsx index f40d5bbd5b013..74d3e57fa7e6b 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/charts/common/anomalies.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/charts/common/anomalies.tsx @@ -7,9 +7,8 @@ import React, { Fragment, FC } from 'react'; import { AnnotationDomainType, LineAnnotation } from '@elastic/charts'; +import { getSeverityColor, ML_ANOMALY_THRESHOLD } from '@kbn/ml-anomaly-utils'; import { Anomaly } from '../../../../common/results_loader'; -import { getSeverityColor } from '../../../../../../../../common/util/anomaly_utils'; -import { ANOMALY_THRESHOLD } from '../../../../../../../../common/constants/anomalies'; interface Props { anomalyData?: Anomaly[]; @@ -61,35 +60,35 @@ export const Anomalies: FC = ({ anomalyData }) => { id="low" domainType={AnnotationDomainType.XDomain} dataValues={severities.low} - style={getAnomalyStyle(ANOMALY_THRESHOLD.LOW)} + style={getAnomalyStyle(ML_ANOMALY_THRESHOLD.LOW)} hideTooltips={true} /> diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/additional_section/components/custom_urls/custom_urls_selection.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/additional_section/components/custom_urls/custom_urls_selection.tsx index 8c483d505aca0..cbdda03eef4f1 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/additional_section/components/custom_urls/custom_urls_selection.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/additional_section/components/custom_urls/custom_urls_selection.tsx @@ -6,8 +6,8 @@ */ import React, { FC, useContext } from 'react'; +import type { MlUrlConfig } from '@kbn/ml-anomaly-utils'; import { CustomUrls } from '../../../../../../../../../components/custom_urls/custom_urls'; -import { UrlConfig } from '../../../../../../../../../../../common/types/custom_urls'; import { JobCreatorContext } from '../../../../../job_creator_context'; import { Description } from './description'; import { CombinedJob } from '../../../../../../../../../../../common/types/anomaly_detection_jobs'; @@ -15,7 +15,7 @@ import { CombinedJob } from '../../../../../../../../../../../common/types/anoma export const CustomUrlsSelection: FC = () => { const { jobCreator, jobCreatorUpdate } = useContext(JobCreatorContext); - const setCustomUrls = (customUrls: UrlConfig[]) => { + const setCustomUrls = (customUrls: MlUrlConfig[]) => { jobCreator.customUrls = customUrls; jobCreatorUpdate(); }; diff --git a/x-pack/plugins/ml/public/application/model_management/models_list.tsx b/x-pack/plugins/ml/public/application/model_management/models_list.tsx index 391136f3f2ada..80c8bef6971f5 100644 --- a/x-pack/plugins/ml/public/application/model_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/model_management/models_list.tsx @@ -39,6 +39,7 @@ import { CURATED_MODEL_TYPE, MODEL_STATE, } from '@kbn/ml-trained-models-utils/src/constants/trained_models'; +import { TechnicalPreviewBadge } from '../components/technical_preview_badge'; import { useModelActions } from './model_actions'; import { ModelsTableToConfigMapping } from '.'; import { ModelsBarStats, StatsBar } from '../components/stats_bar'; @@ -407,6 +408,16 @@ export const ModelsList: FC = ({ sortable: false, truncateText: false, 'data-test-subj': 'mlModelsTableColumnDescription', + render: (description: string) => { + if (!description) return null; + const isTechPreview = description.includes('(Tech Preview)'); + return ( + <> + {description.replace('(Tech Preview)', '')} + {isTechPreview ? : null} + + ); + }, }, { field: ModelsTableToConfigMapping.type, diff --git a/x-pack/plugins/ml/public/application/routing/routes/explorer.tsx b/x-pack/plugins/ml/public/application/routing/routes/explorer.tsx index 99c3f3ff38a68..3fa54aa4abe56 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/explorer.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/explorer.tsx @@ -14,6 +14,7 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { EuiThemeProvider as StyledComponentsThemeProvider } from '@kbn/kibana-react-plugin/common'; import { useUrlState } from '@kbn/ml-url-state'; import { useTimefilter } from '@kbn/ml-date-picker'; +import { ML_JOB_ID } from '@kbn/ml-anomaly-utils'; import { ML_PAGES } from '../../../locator'; import { NavigateToPath, useMlKibana } from '../../contexts/kibana'; @@ -33,7 +34,6 @@ import { useJobSelection } from '../../components/job_selector/use_job_selection import { useTableInterval } from '../../components/controls/select_interval'; import { useTableSeverity } from '../../components/controls/select_severity'; import { getBreadcrumbWithUrlForApp } from '../breadcrumbs'; -import { JOB_ID } from '../../../../common/constants/anomalies'; import { MlAnnotationUpdatesContext } from '../../contexts/ml/ml_annotation_updates_context'; import { AnnotationUpdatesService } from '../../services/annotations_service'; import { useTimeBuckets } from '../../components/custom_hooks/use_time_buckets'; @@ -139,7 +139,7 @@ const ExplorerUrlStateManager: FC = ({ jobsWithTim try { const fetchedStoppedPartitions = await ml.results.getCategoryStoppedPartitions( selectedJobIds, - JOB_ID + ML_JOB_ID ); if ( fetchedStoppedPartitions && diff --git a/x-pack/plugins/ml/public/application/services/anomaly_explorer_charts_service.ts b/x-pack/plugins/ml/public/application/services/anomaly_explorer_charts_service.ts index 6a0f3c8c692b0..676927365e581 100644 --- a/x-pack/plugins/ml/public/application/services/anomaly_explorer_charts_service.ts +++ b/x-pack/plugins/ml/public/application/services/anomaly_explorer_charts_service.ts @@ -11,18 +11,19 @@ import type { TimeRange } from '@kbn/es-query'; import type { TimefilterContract } from '@kbn/data-plugin/public'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import { isDefined } from '@kbn/ml-is-defined'; -import type { RecordForInfluencer } from './results_service/results_service'; -import type { EntityField } from '../../../common/util/anomaly_utils'; +import type { MlEntityField, MlRecordForInfluencer } from '@kbn/ml-anomaly-utils'; import type { CombinedJob } from '../../../common/types/anomaly_detection_jobs'; -import type { MlApiServices } from './ml_api_service'; -import type { MlResultsService } from './results_service'; +import type { InfluencersFilterQuery } from '../../../common/types/es_client'; +import type { SeriesConfigWithMetadata } from '../../../common/types/results'; + import { ExplorerChartsData } from '../explorer/explorer_charts/explorer_charts_container_service'; import type { TimeRangeBounds } from '../util/time_buckets'; import type { AppStateSelectedCells } from '../explorer/explorer_utils'; -import type { InfluencersFilterQuery } from '../../../common/types/es_client'; -import type { SeriesConfigWithMetadata } from '../../../common/types/results'; import { SWIM_LANE_LABEL_WIDTH } from '../explorer/swimlane_container'; +import type { MlApiServices } from './ml_api_service'; +import type { MlResultsService } from './results_service'; + const MAX_CHARTS_PER_ROW = 4; const OPTIMAL_CHART_WIDTH = 550; @@ -71,10 +72,10 @@ export class AnomalyExplorerChartsService { jobIds: string[], earliestMs: number, latestMs: number, - influencers: EntityField[] = [], + influencers: MlEntityField[] = [], selectedCells: AppStateSelectedCells | undefined | null, influencersFilterQuery: InfluencersFilterQuery - ): Observable { + ): Observable { if (!selectedCells && influencers.length === 0 && influencersFilterQuery === undefined) { of([]); } @@ -90,12 +91,12 @@ export class AnomalyExplorerChartsService { influencersFilterQuery ) .pipe( - mapObservable((resp): RecordForInfluencer[] => { + mapObservable((resp): MlRecordForInfluencer[] => { if (isPopulatedObject(selectedCells) || influencersFilterQuery !== undefined) { return resp.records; } - return [] as RecordForInfluencer[]; + return [] as MlRecordForInfluencer[]; }) ); } @@ -106,7 +107,7 @@ export class AnomalyExplorerChartsService { selectedEarliestMs: number, selectedLatestMs: number, influencerFilterQuery?: InfluencersFilterQuery, - influencers?: EntityField[], + influencers?: MlEntityField[], severity = 0, maxSeries?: number ): Observable { diff --git a/x-pack/plugins/ml/public/application/services/anomaly_timeline_service.ts b/x-pack/plugins/ml/public/application/services/anomaly_timeline_service.ts index 38bf80be1ffed..11c8a65262a4f 100644 --- a/x-pack/plugins/ml/public/application/services/anomaly_timeline_service.ts +++ b/x-pack/plugins/ml/public/application/services/anomaly_timeline_service.ts @@ -9,6 +9,7 @@ import { IUiSettingsClient } from '@kbn/core/public'; import type { TimeRange } from '@kbn/es-query'; import { TimefilterContract, UI_SETTINGS } from '@kbn/data-plugin/public'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import type { MlEntityField } from '@kbn/ml-anomaly-utils'; import { getBoundsRoundedToInterval, TimeBuckets, @@ -23,7 +24,6 @@ import { } from '../explorer/explorer_utils'; import { OVERALL_LABEL, VIEW_BY_JOB_LABEL } from '../explorer/explorer_constants'; import { MlResultsService } from './results_service'; -import { EntityField } from '../../../common/util/anomaly_utils'; import { InfluencersFilterQuery } from '../../../common/types/es_client'; /** @@ -260,7 +260,7 @@ export class AnomalyTimelineService { perPage: number, fromPage: number, bucketInterval: TimeBucketsInterval, - selectionInfluencers: EntityField[], + selectionInfluencers: MlEntityField[], influencersFilterQuery: InfluencersFilterQuery ) { const selectedJobIds = selectedJobs.map((d) => d.id); diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/results.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/results.ts index bbc2f36605483..ee74bedb795ac 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/results.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/results.ts @@ -6,26 +6,33 @@ */ // Service for obtaining data for the ML Results dashboards. + import { useMemo } from 'react'; import type { ESSearchRequest, ESSearchResponse } from '@kbn/es-types'; -import { HttpService } from '../http_service'; -import { useMlKibana } from '../../contexts/kibana'; - -import type { CriteriaField } from '../results_service'; -import { basePath } from '.'; -import { JOB_ID, PARTITION_FIELD_VALUE } from '../../../../common/constants/anomalies'; +import type { MlEntityField } from '@kbn/ml-anomaly-utils'; + +import { + type MlAnomalyRecordDoc, + MLAnomalyDoc, + ML_JOB_ID, + ML_PARTITION_FIELD_VALUE, +} from '@kbn/ml-anomaly-utils'; import type { GetStoppedPartitionResult, GetDatafeedResultsChartDataResult, } from '../../../../common/types/results'; import type { JobId } from '../../../../common/types/anomaly_detection_jobs'; -import type { PartitionFieldsDefinition } from '../results_service/result_service_rx'; import type { PartitionFieldsConfig } from '../../../../common/types/storage'; -import type { AnomalyRecordDoc, MLAnomalyDoc } from '../../../../common/types/anomalies'; -import type { EntityField } from '../../../../common/util/anomaly_utils'; import type { InfluencersFilterQuery } from '../../../../common/types/es_client'; import type { ExplorerChartsData } from '../../../../common/types/results'; +import { useMlKibana } from '../../contexts/kibana'; +import type { HttpService } from '../http_service'; +import type { CriteriaField } from '../results_service'; +import type { PartitionFieldsDefinition } from '../results_service/result_service_rx'; + +import { basePath } from '.'; + export interface CategoryDefinition { categoryId: number; terms: string; @@ -37,7 +44,7 @@ export const resultsApiProvider = (httpService: HttpService) => ({ getAnomaliesTableData( jobIds: string[], criteriaFields: string[], - influencers: EntityField[], + influencers: MlEntityField[], aggregationInterval: string, threshold: number, earliestMs: number, @@ -148,7 +155,7 @@ export const resultsApiProvider = (httpService: HttpService) => ({ getCategoryStoppedPartitions( jobIds: string[], - fieldToBucket?: typeof JOB_ID | typeof PARTITION_FIELD_VALUE + fieldToBucket?: typeof ML_JOB_ID | typeof ML_PARTITION_FIELD_VALUE ) { const body = JSON.stringify({ jobIds, @@ -176,7 +183,7 @@ export const resultsApiProvider = (httpService: HttpService) => ({ getAnomalyCharts$( jobIds: string[], - influencers: EntityField[], + influencers: MlEntityField[], threshold: number, earliestMs: number, latestMs: number, @@ -221,7 +228,7 @@ export const resultsApiProvider = (httpService: HttpService) => ({ interval, functionDescription, }); - return httpService.http$<{ success: boolean; records: AnomalyRecordDoc[] }>({ + return httpService.http$<{ success: boolean; records: MlAnomalyRecordDoc[] }>({ path: `${basePath()}/results/anomaly_records`, method: 'POST', body, diff --git a/x-pack/plugins/ml/public/application/services/results_service/result_service_rx.ts b/x-pack/plugins/ml/public/application/services/results_service/result_service_rx.ts index c24ae573a9514..08f82be101dcf 100644 --- a/x-pack/plugins/ml/public/application/services/results_service/result_service_rx.ts +++ b/x-pack/plugins/ml/public/application/services/results_service/result_service_rx.ts @@ -8,7 +8,7 @@ // Queries Elasticsearch to obtain metric aggregation results. // index can be a String, or String[], of index names to search. // entityFields parameter must be an array, with each object in the array having 'fieldName' -// and 'fieldValue' properties. +// and 'fieldValue' properties. // Extra query object can be supplied, or pass null if no additional query // to that built from the supplied entity fields. // Returned response contains a results property containing the requested aggregation. @@ -17,18 +17,21 @@ import { map } from 'rxjs/operators'; import { each, get } from 'lodash'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import type { ErrorType } from '@kbn/ml-error-utils'; +import { + aggregationTypeTransform, + type MlEntityField, + type MlRecordForInfluencer, +} from '@kbn/ml-anomaly-utils'; import { Dictionary } from '../../../../common/types/common'; import { ML_MEDIAN_PERCENTS } from '../../../../common/util/job_utils'; import { Datafeed, JobId } from '../../../../common/types/anomaly_detection_jobs'; -import { MlApiServices } from '../ml_api_service'; -import { CriteriaField } from '.'; import { findAggField } from '../../../../common/util/validation_utils'; import { getDatafeedAggregations } from '../../../../common/util/datafeed_utils'; -import { aggregationTypeTransform, EntityField } from '../../../../common/util/anomaly_utils'; import { ES_AGGREGATION } from '../../../../common/constants/aggregation_types'; import { InfluencersFilterQuery } from '../../../../common/types/es_client'; -import { RecordForInfluencer } from './results_service'; import { isRuntimeMappings } from '../../../../common'; +import { MlApiServices } from '../ml_api_service'; +import { CriteriaField } from '.'; export interface ResultResponse { success: boolean; @@ -648,14 +651,14 @@ export function resultsServiceRxProvider(mlApiServices: MlApiServices) { // Pass an empty array or ['*'] to search over all job IDs. getRecordsForInfluencer$( jobIds: string[], - influencers: EntityField[], + influencers: MlEntityField[], threshold: number, earliestMs: number, latestMs: number, maxResults: number, influencersFilterQuery: InfluencersFilterQuery - ): Observable<{ records: RecordForInfluencer[]; success: boolean }> { - const obj = { success: true, records: [] as RecordForInfluencer[] }; + ): Observable<{ records: MlRecordForInfluencer[]; success: boolean }> { + const obj = { success: true, records: [] as MlRecordForInfluencer[] }; // Build the criteria to use in the bool filter part of the request. // Add criteria for the time range, record score, plus any specified job IDs. diff --git a/x-pack/plugins/ml/public/application/services/results_service/results_service.d.ts b/x-pack/plugins/ml/public/application/services/results_service/results_service.d.ts index e18eb309a1987..97e9e9202d3dd 100644 --- a/x-pack/plugins/ml/public/application/services/results_service/results_service.d.ts +++ b/x-pack/plugins/ml/public/application/services/results_service/results_service.d.ts @@ -5,14 +5,12 @@ * 2.0. */ +import type { EntityField } from '@kbn/ml-anomaly-utils'; import { IndicesOptions } from '../../../../common/types/anomaly_detection_jobs'; -import { MlApiServices } from '../ml_api_service'; -import type { AnomalyRecordDoc } from '../../../../common/types/anomalies'; import { InfluencersFilterQuery } from '../../../../common/types/es_client'; -import { EntityField } from '../../../../common/util/anomaly_utils'; import { RuntimeMappings } from '../../../../common/types/fields'; +import { MlApiServices } from '../ml_api_service'; -export type RecordForInfluencer = AnomalyRecordDoc; export function resultsServiceProvider(mlApiServices: MlApiServices): { getScoresByBucket( jobIds: string[], diff --git a/x-pack/plugins/ml/public/application/services/results_service/results_service.js b/x-pack/plugins/ml/public/application/services/results_service/results_service.js index 11ec3b821c9f4..f7074a3498b75 100644 --- a/x-pack/plugins/ml/public/application/services/results_service/results_service.js +++ b/x-pack/plugins/ml/public/application/services/results_service/results_service.js @@ -15,7 +15,7 @@ import { ANOMALY_SWIM_LANE_HARD_LIMIT, SWIM_LANE_DEFAULT_PAGE_SIZE, } from '../../explorer/explorer_constants'; -import { aggregationTypeTransform } from '../../../../common/util/anomaly_utils'; +import { aggregationTypeTransform } from '@kbn/ml-anomaly-utils'; /** * Service for carrying out Elasticsearch queries to obtain data for the Ml Results dashboards. diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/entity_control/entity_config.tsx b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/entity_control/entity_config.tsx index b7892f4797d48..3133a690d34e7 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/entity_control/entity_config.tsx +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/entity_control/entity_config.tsx @@ -21,15 +21,15 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import type { MlEntityFieldType } from '@kbn/ml-anomaly-utils'; import { Entity } from './entity_control'; import { UiPartitionFieldConfig } from '../series_controls/series_controls'; -import { EntityFieldType } from '../../../../../common/types/anomalies'; interface EntityConfigProps { entity: Entity; isModelPlotEnabled: boolean; config: UiPartitionFieldConfig; - onConfigChange: (fieldType: EntityFieldType, config: Partial) => void; + onConfigChange: (fieldType: MlEntityFieldType, config: Partial) => void; } export const EntityConfig: FC = ({ diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/entity_control/entity_control.tsx b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/entity_control/entity_control.tsx index e688a8a27de66..c23edfb394194 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/entity_control/entity_control.tsx +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/entity_control/entity_control.tsx @@ -18,14 +18,13 @@ import { EuiHealth, EuiHighlight, } from '@elastic/eui'; -import { EntityFieldType } from '../../../../../common/types/anomalies'; +import { getSeverityColor, type MlEntityFieldType } from '@kbn/ml-anomaly-utils'; import { UiPartitionFieldConfig } from '../series_controls/series_controls'; -import { getSeverityColor } from '../../../../../common'; import { EntityConfig } from './entity_config'; export interface Entity { fieldName: string; - fieldType: EntityFieldType; + fieldType: MlEntityFieldType; fieldValue: any; fieldValues?: any; } @@ -48,7 +47,7 @@ export interface EntityControlProps { isLoading: boolean; onSearchChange: (entity: Entity, queryTerm: string) => void; config: UiPartitionFieldConfig; - onConfigChange: (fieldType: EntityFieldType, config: Partial) => void; + onConfigChange: (fieldType: MlEntityFieldType, config: Partial) => void; forceSelection: boolean; options: ComboBoxOption[]; isModelPlotEnabled: boolean; diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/series_controls/series_controls.tsx b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/series_controls/series_controls.tsx index 8d79a41b0ab36..23bc2f80eb1a8 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/series_controls/series_controls.tsx +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/series_controls/series_controls.tsx @@ -11,6 +11,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiSelect, EuiSelectProps } from import { debounce } from 'lodash'; import { lastValueFrom } from 'rxjs'; import { useStorage } from '@kbn/ml-local-storage'; +import type { MlEntityFieldType } from '@kbn/ml-anomaly-utils'; import { EntityControl } from '../entity_control'; import { mlJobService } from '../../../services/job_service'; import { Detector, JobId } from '../../../../../common/types/anomaly_detection_jobs'; @@ -29,7 +30,6 @@ import { type MlStorageKey, type TMlStorageMapped, } from '../../../../../common/types/storage'; -import { EntityFieldType } from '../../../../../common/types/anomalies'; import { FieldDefinition } from '../../../services/results_service/result_service_rx'; import { getViewableDetectors } from '../../timeseriesexplorer_utils/get_viewable_detectors'; import { PlotByFunctionControls } from '../plot_function_controls'; @@ -52,7 +52,7 @@ export type UiPartitionFieldConfig = Exclude; * Provides default fields configuration. */ const getDefaultFieldConfig = ( - fieldTypes: EntityFieldType[], + fieldTypes: MlEntityFieldType[], isAnomalousOnly: boolean, applyTimeRange: boolean ): UiPartitionFieldsConfig => { @@ -250,7 +250,7 @@ export const SeriesControls: FC = ({ // we need to change it for all the other fields for (const c in updatedResultConfig) { if (updatedResultConfig.hasOwnProperty(c)) { - updatedResultConfig[c as EntityFieldType]!.anomalousOnly = + updatedResultConfig[c as MlEntityFieldType]!.anomalousOnly = updatedFieldConfig.anomalousOnly; } } @@ -261,7 +261,7 @@ export const SeriesControls: FC = ({ // we need to change it for all the other fields for (const c in updatedResultConfig) { if (updatedResultConfig.hasOwnProperty(c)) { - updatedResultConfig[c as EntityFieldType]!.applyTimeRange = + updatedResultConfig[c as MlEntityFieldType]!.applyTimeRange = updatedFieldConfig.applyTimeRange; } } diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js index 707511c1f22d4..4c7af8120a482 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js @@ -18,10 +18,7 @@ import d3 from 'd3'; import moment from 'moment'; import { i18n } from '@kbn/i18n'; -import { - getFormattedSeverityScore, - getSeverityWithLow, -} from '../../../../../common/util/anomaly_utils'; +import { getFormattedSeverityScore, getSeverityWithLow } from '@kbn/ml-anomaly-utils'; import { formatValue } from '../../../formatters/format_value'; import { LINE_CHART_ANOMALY_RADIUS, diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseries_search_service.ts b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseries_search_service.ts index 2f879ded72e02..044d166ca5efa 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseries_search_service.ts +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseries_search_service.ts @@ -9,6 +9,7 @@ import { each, find, get, filter } from 'lodash'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; +import type { MlEntityField } from '@kbn/ml-anomaly-utils'; import { ml } from '../services/ml_api_service'; import { isModelPlotChartableForDetector, @@ -19,12 +20,11 @@ import { buildConfigFromDetector } from '../util/chart_config_builder'; import { mlResultsService } from '../services/results_service'; import { ModelPlotOutput } from '../services/results_service/result_service_rx'; import { Job } from '../../../common/types/anomaly_detection_jobs'; -import { EntityField } from '../../../common/util/anomaly_utils'; function getMetricData( job: Job, detectorIndex: number, - entityFields: EntityField[], + entityFields: MlEntityField[], earliestMs: number, latestMs: number, intervalMs: number, diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js index 0725ce13d4b12..a73926921fd27 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js @@ -82,7 +82,7 @@ import { ANOMALY_DETECTION_DEFAULT_TIME_RANGE } from '../../../common/constants/ import { getControlsForDetector } from './get_controls_for_detector'; import { SeriesControls } from './components/series_controls'; import { TimeSeriesChartWithTooltips } from './components/timeseries_chart/timeseries_chart_with_tooltip'; -import { aggregationTypeTransform } from '../../../common/util/anomaly_utils'; +import { aggregationTypeTransform } from '@kbn/ml-anomaly-utils'; import { isMetricDetector } from './get_function_description'; import { getViewableDetectors } from './timeseriesexplorer_utils/get_viewable_detectors'; import { TimeseriesexplorerChartDataError } from './components/timeseriesexplorer_chart_data_error'; diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/get_focus_data.ts b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/get_focus_data.ts index 7a7837ba71435..a70530f476ba0 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/get_focus_data.ts +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/get_focus_data.ts @@ -8,6 +8,7 @@ import { forkJoin, Observable, of } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import { extractErrorMessage } from '@kbn/ml-error-utils'; +import { aggregationTypeTransform } from '@kbn/ml-anomaly-utils'; import { ml } from '../../services/ml_api_service'; import { ANNOTATIONS_TABLE_DEFAULT_QUERY_SIZE } from '../../../../common/constants/search'; import { mlTimeSeriesSearchService } from '../timeseries_search_service'; @@ -23,7 +24,6 @@ import { import { mlForecastService } from '../../services/forecast_service'; import { mlFunctionToESAggregation } from '../../../../common/util/job_utils'; import { GetAnnotationsResponse } from '../../../../common/types/annotations'; -import { aggregationTypeTransform } from '../../../../common/util/anomaly_utils'; export interface Interval { asMilliseconds: () => number; diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/timeseriesexplorer_utils.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/timeseriesexplorer_utils.js index e18ee2fa98b75..a0b20888939aa 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/timeseriesexplorer_utils.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/timeseriesexplorer_utils.js @@ -14,14 +14,14 @@ import { each, get, find } from 'lodash'; import moment from 'moment-timezone'; -import { isMultiBucketAnomaly } from '../../../../common/util/anomaly_utils'; +import { isMultiBucketAnomaly } from '@kbn/ml-anomaly-utils'; + import { isTimeSeriesViewJob } from '../../../../common/util/job_utils'; import { parseInterval } from '../../../../common/util/parse_interval'; +import { ML_JOB_AGGREGATION } from '../../../../common/constants/aggregation_types'; import { getBoundsRoundedToInterval, getTimeBucketsFromCache } from '../../util/time_buckets'; - import { CHARTS_POINT_TARGET, TIME_FIELD_NAME } from '../timeseriesexplorer_constants'; -import { ML_JOB_AGGREGATION } from '../../../../common/constants/aggregation_types'; // create new job objects based on standard job config objects // new job objects just contain job id, bucket span in seconds and a selected flag. diff --git a/x-pack/plugins/ml/public/application/util/chart_utils.js b/x-pack/plugins/ml/public/application/util/chart_utils.js index ab842fe6f688d..19b53177d6868 100644 --- a/x-pack/plugins/ml/public/application/util/chart_utils.js +++ b/x-pack/plugins/ml/public/application/util/chart_utils.js @@ -6,8 +6,8 @@ */ import d3 from 'd3'; +import { getAnomalyScoreExplanationImpactValue } from '@kbn/ml-anomaly-utils'; import { calculateTextWidth } from './string_utils'; -import { getAnomalyScoreExplanationImpactValue } from '../../../common/util/anomaly_utils'; import moment from 'moment'; import { CHART_TYPE } from '../explorer/explorer_constants'; import { ML_PAGES } from '../../../common/constants/locator'; diff --git a/x-pack/plugins/ml/public/application/util/custom_url_utils.test.ts b/x-pack/plugins/ml/public/application/util/custom_url_utils.test.ts index 03524f5be3063..359aaf1ce6074 100644 --- a/x-pack/plugins/ml/public/application/util/custom_url_utils.test.ts +++ b/x-pack/plugins/ml/public/application/util/custom_url_utils.test.ts @@ -5,6 +5,13 @@ * 2.0. */ +import type { + MlAnomalyRecordDoc, + MlCustomUrlAnomalyRecordDoc, + MlKibanaUrlConfig, + MlUrlConfig, +} from '@kbn/ml-anomaly-utils'; + import { replaceTokensInUrlValue, getUrlForRecord, @@ -13,15 +20,9 @@ import { openCustomUrlWindow, getQueryField, } from './custom_url_utils'; -import { AnomalyRecordDoc } from '../../../common/types/anomalies'; -import { - CustomUrlAnomalyRecordDoc, - KibanaUrlConfig, - UrlConfig, -} from '../../../common/types/custom_urls'; describe('ML - custom URL utils', () => { - const TEST_DOC: AnomalyRecordDoc = { + const TEST_DOC: MlAnomalyRecordDoc = { job_id: 'farequote', result_type: 'record', probability: 6.533287347648861e-45, @@ -47,7 +48,7 @@ describe('ML - custom URL utils', () => { airline: ['AAL'], }; - const TEST_RECORD: CustomUrlAnomalyRecordDoc = { + const TEST_RECORD: MlCustomUrlAnomalyRecordDoc = { ...TEST_DOC, earliest: '2017-02-09T15:10:00.000Z', latest: '2017-02-09T17:15:00.000Z', @@ -72,7 +73,7 @@ describe('ML - custom URL utils', () => { 'odd:field,name': [">:&12<'"], }; - const TEST_RECORD_MULTIPLE_INFLUENCER_VALUES: CustomUrlAnomalyRecordDoc = { + const TEST_RECORD_MULTIPLE_INFLUENCER_VALUES: MlCustomUrlAnomalyRecordDoc = { ...TEST_RECORD, influencers: [ { @@ -83,7 +84,7 @@ describe('ML - custom URL utils', () => { airline: ['AAL', 'AWE'], }; - const TEST_RECORD_NO_INFLUENCER_VALUES: CustomUrlAnomalyRecordDoc = { + const TEST_RECORD_NO_INFLUENCER_VALUES: MlCustomUrlAnomalyRecordDoc = { ...TEST_RECORD, influencers: [ { @@ -94,33 +95,33 @@ describe('ML - custom URL utils', () => { airline: null, }; - const TEST_DASHBOARD_URL: KibanaUrlConfig = { + const TEST_DASHBOARD_URL: MlKibanaUrlConfig = { url_name: 'Show dashboard', time_range: '1h', url_value: "dashboards#/view/5f112420-9fc6-11e8-9130-150552a4bef3?_g=(time:(from:'$earliest$',mode:absolute,to:'$latest$'))&_a=(filters:!(),query:(language:kuery,query:'airline:\"$airline$\"'))", }; - const TEST_DISCOVER_URL: KibanaUrlConfig = { + const TEST_DISCOVER_URL: MlKibanaUrlConfig = { url_name: 'Raw data', time_range: 'auto', url_value: "discover#/?_g=(time:(from:'$earliest$',mode:absolute,to:'$latest$'))&_a=(index:bf6e5860-9404-11e8-8d4c-593f69c47267,query:(language:kuery,query:'airline:\"$airline$\" and odd:field,name : $odd:field,name$'))", }; - const TEST_DASHBOARD_LUCENE_URL: KibanaUrlConfig = { + const TEST_DASHBOARD_LUCENE_URL: MlKibanaUrlConfig = { url_name: 'Show dashboard', time_range: '1h', url_value: "dashboards#/view/5f112420-9fc6-11e8-9130-150552a4bef3?_g=(time:(from:'$earliest$',mode:absolute,to:'$latest$'))&_a=(filters:!(),query:(language:lucene,query:'airline:\"$airline$\"'))", }; - const TEST_OTHER_URL: UrlConfig = { + const TEST_OTHER_URL: MlUrlConfig = { url_name: 'Show airline', url_value: 'http://airlinecodes.info/airline-code-$airline$', }; - const TEST_OTHER_URL_NO_TOKENS: UrlConfig = { + const TEST_OTHER_URL_NO_TOKENS: MlUrlConfig = { url_name: 'Show docs', url_value: 'https://www.elastic.co/guide/index.html', }; @@ -198,11 +199,11 @@ describe('ML - custom URL utils', () => { }); test('replaces tokens outside of a query', () => { - const TEST_DOC_WITH_METHOD: AnomalyRecordDoc = { + const TEST_DOC_WITH_METHOD: MlAnomalyRecordDoc = { ...TEST_DOC, method: ['POST'], }; - const TEST_MULTIPLE_NON_QUERY_TOKENS: UrlConfig = { + const TEST_MULTIPLE_NON_QUERY_TOKENS: MlUrlConfig = { url_name: 'no_query', url_value: `dashboards#/view/b3ad9930-db86-11e9-b5d5-e3a9ca224c61?_g=(filters:!(),time:(from:'2018-12-17T00:00:00.000Z',mode:absolute,to:'2018-12-17T09:00:00.000Z'))&_a=(description:'',filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'7e06e310-dae4-11e9-8260-995f99197467',key:method,negate:!f,params:(query:$method$),type:phrase,value:$method$),query:(match:(method:(query:$method$,type:phrase))))))`, }; @@ -219,7 +220,7 @@ describe('ML - custom URL utils', () => { }); test('truncates long queries', () => { - const TEST_DOC_WITH_METHOD: AnomalyRecordDoc = { + const TEST_DOC_WITH_METHOD: MlAnomalyRecordDoc = { ...TEST_DOC, influencers: [ { @@ -240,7 +241,7 @@ describe('ML - custom URL utils', () => { referer: ['http://www.example.com/wp-admin/post.php?post=51&action=edit'], method: 'POST', }; - const TEST_MULTIPLE_NON_QUERY_TOKENS: UrlConfig = { + const TEST_MULTIPLE_NON_QUERY_TOKENS: MlUrlConfig = { url_name: 'massive_url', url_value: `discover#/11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61-b3ad9930-db86-11e9-b5d5-e3a9ca224c61?_g=(filters:!(),time:(from:'$earliest$',mode:absolute,to:'$latest$'))&_a=(columns:!(_source),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'7e06e310-dae4-11e9-8260-995f99197467',key:method,negate:!f,params:(query:$method$),type:phrase,value:$method$),query:(match:(method:(query:$method$,type:phrase))))),index:'7e06e310-dae4-11e9-8260-995f99197467',interval:auto,query:(language:kuery,query:'clientip:$clientip$ and action:$action$ and referer:$referer$'),sort:!(!('@timestamp',desc)))`, }; @@ -323,7 +324,7 @@ describe('ML - custom URL utils', () => { }); test('replaces tokens with nesting', () => { - const testUrlApache: KibanaUrlConfig = { + const testUrlApache: MlKibanaUrlConfig = { url_name: 'Raw data', time_range: 'auto', url_value: @@ -360,7 +361,7 @@ describe('ML - custom URL utils', () => { }); test('does not escape special characters for Lucene query language inside of the filter', () => { - const testUrlLuceneFilters: KibanaUrlConfig = { + const testUrlLuceneFilters: MlKibanaUrlConfig = { url_name: 'Lucene query with filters', time_range: 'auto', url_value: @@ -588,7 +589,7 @@ describe('ML - custom URL utils', () => { }); test('returns expected URL with preserving custom filter', () => { - const urlWithCustomFilter: UrlConfig = { + const urlWithCustomFilter: MlUrlConfig = { url_name: 'URL with a custom filter', url_value: `discover#/?_g=(time:(from:'$earliest$',mode:absolute,to:'$latest$'))&_a=(filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,key:subSystem.keyword,negate:!f,params:(query:JDBC),type:phrase),query:(match_phrase:(subSystem.keyword:JDBC)))),index:'eap_wls_server_12c*,*:eap_wls_server_12c*',query:(language:kuery,query:'wlscluster.keyword:"$wlscluster.keyword$"'))`, }; diff --git a/x-pack/plugins/ml/public/application/util/custom_url_utils.ts b/x-pack/plugins/ml/public/application/util/custom_url_utils.ts index 11c5ee32b499c..c754472fd33e1 100644 --- a/x-pack/plugins/ml/public/application/util/custom_url_utils.ts +++ b/x-pack/plugins/ml/public/application/util/custom_url_utils.ts @@ -11,14 +11,14 @@ import { get, flow } from 'lodash'; import moment from 'moment'; import rison, { type RisonValue } from '@kbn/rison'; import { TimeRange } from '@kbn/es-query'; +import type { + MlAnomalyRecordDoc, + MlUrlConfig, + MlKibanaUrlConfig, + MlCustomUrlAnomalyRecordDoc, +} from '@kbn/ml-anomaly-utils'; import { parseInterval } from '../../../common/util/parse_interval'; import { escapeForElasticsearchQuery, replaceStringTokens } from './string_utils'; -import { - UrlConfig, - KibanaUrlConfig, - CustomUrlAnomalyRecordDoc, -} from '../../../common/types/custom_urls'; -import { AnomalyRecordDoc } from '../../../common/types/anomalies'; import type { DataGridItem } from '../components/data_grid'; // Value of custom_url time_range property indicating drilldown time range is calculated automatically @@ -28,7 +28,7 @@ const TIME_RANGE_AUTO = 'auto'; // Replaces the $ delimited tokens in the url_value of the custom URL configuration // with values from the supplied document. export function replaceTokensInDFAUrlValue( - customUrlConfig: UrlConfig | KibanaUrlConfig, + customUrlConfig: MlUrlConfig | MlKibanaUrlConfig, doc: DataGridItem, timeRange?: TimeRange ) { @@ -49,9 +49,9 @@ export function replaceTokensInDFAUrlValue( // Replaces the $ delimited tokens in the url_value of the custom URL configuration // with values from the supplied document. export function replaceTokensInUrlValue( - customUrlConfig: UrlConfig | KibanaUrlConfig, + customUrlConfig: MlUrlConfig | MlKibanaUrlConfig, jobBucketSpanSecs: number, - doc: AnomalyRecordDoc | Record, + doc: MlAnomalyRecordDoc | Record, timeFieldName: 'timestamp' | string ) { // If urlValue contains $earliest$ and $latest$ tokens, add in times to the test doc. @@ -61,7 +61,7 @@ export function replaceTokensInUrlValue( 'time_range' in customUrlConfig && customUrlConfig.time_range ? parseInterval(customUrlConfig.time_range) : null; - const record = { ...doc } as CustomUrlAnomalyRecordDoc; + const record = { ...doc } as MlCustomUrlAnomalyRecordDoc; if (urlValue.includes('$earliest$')) { const earliestMoment = moment(timestamp); if (timeRangeInterval !== null) { @@ -88,8 +88,8 @@ export function replaceTokensInUrlValue( // Returns the URL to open from the supplied config, with any dollar delimited tokens // substituted from the supplied anomaly record. export function getUrlForRecord( - urlConfig: UrlConfig | KibanaUrlConfig, - record: CustomUrlAnomalyRecordDoc | DataGridItem + urlConfig: MlUrlConfig | MlKibanaUrlConfig, + record: MlCustomUrlAnomalyRecordDoc | DataGridItem ) { if (isKibanaUrl(urlConfig) === true) { return buildKibanaUrl(urlConfig, record); @@ -104,7 +104,7 @@ export function getUrlForRecord( // object which indicates whether it is opening a Kibana page running on the same server. // `url` is the URL with any dollar delimited tokens from the urlConfig // having been substituted with values from an anomaly record. -export function openCustomUrlWindow(url: string, urlConfig: UrlConfig, basePath: string) { +export function openCustomUrlWindow(url: string, urlConfig: MlUrlConfig, basePath: string) { // Run through a regex to test whether the url_value starts with a protocol scheme. if (/^(?:[a-z]+:)?\/\//i.test(urlConfig.url_value) === false) { // If `url` is a relative path, we need to prefix the base path. @@ -132,7 +132,7 @@ export function openCustomUrlWindow(url: string, urlConfig: UrlConfig, basePath: // recognize modules or with custom UI in the custom URL builder we'd // need to add the solution here. Manually created custom URLs for other // solution pages need to be prefixed with `app/` in the custom URL builder. -function isKibanaUrl(urlConfig: UrlConfig) { +function isKibanaUrl(urlConfig: MlUrlConfig) { const urlValue = urlConfig.url_value; return ( // HashRouter based plugins @@ -196,7 +196,7 @@ export const getQueryField = (str: string): string => { return fieldName; }; const getQueryStringResultProvider = - (record: CustomUrlAnomalyRecordDoc | DataGridItem, getResultTokenValue: GetResultTokenValue) => + (record: MlCustomUrlAnomalyRecordDoc | DataGridItem, getResultTokenValue: GetResultTokenValue) => (resultPrefix: string, queryString: string, resultPostfix: string, isKuery: boolean): string => { const URL_LENGTH_LIMIT = 2000; @@ -255,7 +255,10 @@ const getQueryStringResultProvider = * Builds a Kibana dashboard or Discover URL from the supplied config, with any * dollar delimited tokens substituted from the supplied anomaly record. */ -function buildKibanaUrl(urlConfig: UrlConfig, record: CustomUrlAnomalyRecordDoc | DataGridItem) { +function buildKibanaUrl( + urlConfig: MlUrlConfig, + record: MlCustomUrlAnomalyRecordDoc | DataGridItem +) { const urlValue = urlConfig.url_value; const isLuceneQueryLanguage = urlValue.includes('language:lucene'); diff --git a/x-pack/plugins/ml/public/application/util/string_utils.test.ts b/x-pack/plugins/ml/public/application/util/string_utils.test.ts index 6547466231b30..94463e38f9611 100644 --- a/x-pack/plugins/ml/public/application/util/string_utils.test.ts +++ b/x-pack/plugins/ml/public/application/util/string_utils.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { CustomUrlAnomalyRecordDoc } from '../../../common/types/custom_urls'; +import type { MlCustomUrlAnomalyRecordDoc } from '@kbn/ml-anomaly-utils'; import { Detector } from '../../../common/types/anomaly_detection_jobs'; import { @@ -18,7 +18,7 @@ import { describe('ML - string utils', () => { describe('replaceStringTokens', () => { - const testRecord: CustomUrlAnomalyRecordDoc = { + const testRecord: MlCustomUrlAnomalyRecordDoc = { job_id: 'test_job', result_type: 'record', probability: 0.0191711, diff --git a/x-pack/plugins/ml/public/application/util/string_utils.ts b/x-pack/plugins/ml/public/application/util/string_utils.ts index a44aac08f81d1..f4a73ee200e7c 100644 --- a/x-pack/plugins/ml/public/application/util/string_utils.ts +++ b/x-pack/plugins/ml/public/application/util/string_utils.ts @@ -13,7 +13,7 @@ import he from 'he'; import { escapeKuery } from '@kbn/es-query'; import { isDefined } from '@kbn/ml-is-defined'; -import { CustomUrlAnomalyRecordDoc } from '../../../common/types/custom_urls'; +import type { MlCustomUrlAnomalyRecordDoc } from '@kbn/ml-anomaly-utils'; import type { DataGridItem } from '../components/data_grid'; import { Detector } from '../../../common/types/anomaly_detection_jobs'; @@ -26,7 +26,7 @@ import { Detector } from '../../../common/types/anomaly_detection_jobs'; // If a corresponding key is not found in valuesByTokenName, then the String is not replaced. export function replaceStringTokens( str: string, - valuesByTokenName: CustomUrlAnomalyRecordDoc | DataGridItem, + valuesByTokenName: MlCustomUrlAnomalyRecordDoc | DataGridItem, encodeForURI: boolean ) { return String(str).replace(/\$([^?&$\'"]+)\$/g, (match, name) => { diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container.tsx index 98f211e3fc8df..dd83c4fbbf339 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container.tsx @@ -12,6 +12,11 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { throttle } from 'lodash'; import { UI_SETTINGS } from '@kbn/data-plugin/common'; import useObservable from 'react-use/lib/useObservable'; +import { + type MlEntityField, + type MlEntityFieldOperation, + ML_ANOMALY_THRESHOLD, +} from '@kbn/ml-anomaly-utils'; import { useEmbeddableExecutionContext } from '../common/use_embeddable_execution_context'; import { useAnomalyChartsInputResolver } from './use_anomaly_charts_input_resolver'; import type { IAnomalyChartsEmbeddable } from './anomaly_charts_embeddable'; @@ -20,12 +25,10 @@ import type { AnomalyChartsEmbeddableOutput, AnomalyChartsEmbeddableServices, } from '..'; -import type { EntityField, EntityFieldOperation } from '../../../common/util/anomaly_utils'; import { ExplorerAnomaliesContainer } from '../../application/explorer/explorer_charts/explorer_anomalies_container'; import { ML_APP_LOCATOR } from '../../../common/constants/locator'; import { optionValueToThreshold } from '../../application/components/controls/select_severity/select_severity'; -import { ANOMALY_THRESHOLD } from '../../../common'; import { TimeBuckets } from '../../application/util/time_buckets'; import { EXPLORER_ENTITY_FIELD_SELECTION_TRIGGER } from '../../ui_actions/triggers'; import { MlLocatorParams } from '../../../common/types/locator'; @@ -68,10 +71,10 @@ export const EmbeddableAnomalyChartsContainer: FC(0); const [severity, setSeverity] = useState( optionValueToThreshold( - embeddableContext.getInput().severityThreshold ?? ANOMALY_THRESHOLD.WARNING + embeddableContext.getInput().severityThreshold ?? ML_ANOMALY_THRESHOLD.WARNING ) ); - const [selectedEntities, setSelectedEntities] = useState(); + const [selectedEntities, setSelectedEntities] = useState(); const [{ uiSettings }, { data: dataServices, share, uiActions, charts: chartsService }] = services; const { timefilter } = dataServices.query.timefilter; @@ -162,9 +165,9 @@ export const EmbeddableAnomalyChartsContainer: FC { - const entity: EntityField = { + const entity: MlEntityField = { fieldName, fieldValue, operation, diff --git a/x-pack/plugins/ml/public/embeddables/types.ts b/x-pack/plugins/ml/public/embeddables/types.ts index 69a90ac5ef138..74a2e042a9898 100644 --- a/x-pack/plugins/ml/public/embeddables/types.ts +++ b/x-pack/plugins/ml/public/embeddables/types.ts @@ -11,6 +11,7 @@ import type { RefreshInterval } from '@kbn/data-plugin/common'; import type { EmbeddableInput, EmbeddableOutput, IEmbeddable } from '@kbn/embeddable-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/common'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import type { MlEntityField } from '@kbn/ml-anomaly-utils'; import type { JobId } from '../../common/types/anomaly_detection_jobs'; import type { SwimlaneType } from '../application/explorer/explorer_constants'; import type { AnomalyDetectorService } from '../application/services/anomaly_detector_service'; @@ -18,7 +19,6 @@ import type { AnomalyTimelineService } from '../application/services/anomaly_tim import type { MlDependencies } from '../application/app'; import type { AppStateSelectedCells } from '../application/explorer/explorer_utils'; import { AnomalyExplorerChartsService } from '../application/services/anomaly_explorer_charts_service'; -import { EntityField } from '../../common/util/anomaly_utils'; import { ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE, ANOMALY_SWIMLANE_EMBEDDABLE_TYPE, @@ -107,7 +107,7 @@ export interface AnomalyChartsServices { export type AnomalyChartsEmbeddableServices = [CoreStart, MlDependencies, AnomalyChartsServices]; export interface AnomalyChartsCustomOutput { - entityFields?: EntityField[]; + entityFields?: MlEntityField[]; severity?: number; indexPatterns?: DataView[]; } @@ -119,7 +119,7 @@ export interface AnomalyChartsFieldSelectionContext extends EditAnomalyChartsPan /** * Optional fields selected using anomaly charts */ - data?: EntityField[]; + data?: MlEntityField[]; } export function isAnomalyExplorerEmbeddable( arg: unknown diff --git a/x-pack/plugins/ml/public/index.ts b/x-pack/plugins/ml/public/index.ts index ed99376a51c91..770a268442ba0 100755 --- a/x-pack/plugins/ml/public/index.ts +++ b/x-pack/plugins/ml/public/index.ts @@ -27,9 +27,7 @@ export const plugin: PluginInitializer< export type { MlPluginSetup, MlPluginStart }; export type { - AnomaliesTableRecord, DataRecognizerConfigResponse, - Influencer, JobExistResult, JobStat, MlCapabilitiesResponse, @@ -44,16 +42,8 @@ export type { AnomalySwimlaneEmbeddableInput } from './embeddables'; export { ANOMALY_SWIMLANE_EMBEDDABLE_TYPE } from './embeddables/constants'; export { CONTROLLED_BY_SWIM_LANE_FILTER } from './ui_actions/constants'; -// Static exports -export { - getSeverityColor, - getSeverityType, - getFormattedSeverityScore, - getSeverity, -} from '../common/util/anomaly_utils'; export { ES_CLIENT_TOTAL_HITS_RELATION } from '../common/types/es_client'; -export { ANOMALY_SEVERITY } from '../common'; export type { MlLocator } from './locator'; export { useMlHref, ML_PAGES, MlLocatorDefinition } from './locator'; diff --git a/x-pack/plugins/ml/public/maps/util.ts b/x-pack/plugins/ml/public/maps/util.ts index 00a1d75295409..c070d352ab99e 100644 --- a/x-pack/plugins/ml/public/maps/util.ts +++ b/x-pack/plugins/ml/public/maps/util.ts @@ -18,10 +18,9 @@ import { fromKueryExpression, luceneStringToDsl, toElasticsearchQuery } from '@k import type { ESSearchResponse } from '@kbn/es-types'; import { VectorSourceRequestMeta } from '@kbn/maps-plugin/common'; import { LAYER_TYPE, SOURCE_TYPES, SCALING_TYPES } from '@kbn/maps-plugin/common'; -import { SEVERITY_COLOR_RAMP } from '../../common'; +import { type MLAnomalyDoc, ML_SEVERITY_COLOR_RAMP } from '@kbn/ml-anomaly-utils'; import { formatHumanReadableDateTimeSeconds } from '../../common/util/date_utils'; import type { MlApiServices } from '../application/services/ml_api_service'; -import { MLAnomalyDoc } from '../../common/types/anomalies'; import { SEARCH_QUERY_LANGUAGE } from '../../common/constants/search'; import { tabColor } from '../../common/util/group_color_utils'; import { getIndexPattern } from '../application/explorer/reducers/explorer_reducer/get_index_pattern'; @@ -37,7 +36,7 @@ export const ML_ANOMALY_LAYERS = { export const CUSTOM_COLOR_RAMP = { type: STYLE_TYPE.DYNAMIC, options: { - customColorRamp: SEVERITY_COLOR_RAMP, + customColorRamp: ML_SEVERITY_COLOR_RAMP, field: { name: 'record_score', origin: FIELD_ORIGIN.SOURCE, diff --git a/x-pack/plugins/ml/public/shared.ts b/x-pack/plugins/ml/public/shared.ts index 1d19fee5c8392..0a1db5660237a 100644 --- a/x-pack/plugins/ml/public/shared.ts +++ b/x-pack/plugins/ml/public/shared.ts @@ -5,16 +5,12 @@ * 2.0. */ -export * from '../common/constants/anomalies'; - export * from '../common/types/data_recognizer'; export * from '../common/types/capabilities'; -export * from '../common/types/anomalies'; export * from '../common/types/anomaly_detection_jobs'; export * from '../common/types/modules'; export * from '../common/types/audit_message'; -export * from '../common/util/anomaly_utils'; export * from '../common/util/validators'; export * from '../common/util/date_utils'; diff --git a/x-pack/plugins/ml/public/ui_actions/apply_entity_filters_action.tsx b/x-pack/plugins/ml/public/ui_actions/apply_entity_filters_action.tsx index ec823fd13ea46..6ee0ad3fbdb70 100644 --- a/x-pack/plugins/ml/public/ui_actions/apply_entity_filters_action.tsx +++ b/x-pack/plugins/ml/public/ui_actions/apply_entity_filters_action.tsx @@ -8,13 +8,13 @@ import { Filter, FilterStateStore } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import type { UiActionsActionDefinition } from '@kbn/ui-actions-plugin/public'; +import { ML_ENTITY_FIELD_OPERATIONS } from '@kbn/ml-anomaly-utils'; import { MlCoreSetup } from '../plugin'; import { ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE, AnomalyChartsFieldSelectionContext, } from '../embeddables'; import { CONTROLLED_BY_ANOMALY_CHARTS_FILTER } from './constants'; -import { ENTITY_FIELD_OPERATIONS } from '../../common/util/anomaly_utils'; export const APPLY_ENTITY_FIELD_FILTERS_ACTION = 'applyEntityFieldFiltersAction'; @@ -41,7 +41,7 @@ export function createApplyEntityFieldFiltersAction( filterManager.addFilters( data - .filter((d) => d.operation === ENTITY_FIELD_OPERATIONS.ADD) + .filter((d) => d.operation === ML_ENTITY_FIELD_OPERATIONS.ADD) .map(({ fieldName, fieldValue }) => { return { $state: { @@ -73,7 +73,7 @@ export function createApplyEntityFieldFiltersAction( ); data - .filter((field) => field.operation === ENTITY_FIELD_OPERATIONS.REMOVE) + .filter((field) => field.operation === ML_ENTITY_FIELD_OPERATIONS.REMOVE) .forEach((field) => { const filter = filterManager .getFilters() diff --git a/x-pack/plugins/ml/public/ui_actions/open_in_anomaly_explorer_action.tsx b/x-pack/plugins/ml/public/ui_actions/open_in_anomaly_explorer_action.tsx index b4a21ad621b4b..ad7af5f05a75a 100644 --- a/x-pack/plugins/ml/public/ui_actions/open_in_anomaly_explorer_action.tsx +++ b/x-pack/plugins/ml/public/ui_actions/open_in_anomaly_explorer_action.tsx @@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n'; import { SerializableRecord } from '@kbn/utility-types'; import type { UiActionsActionDefinition } from '@kbn/ui-actions-plugin/public'; +import { ML_ENTITY_FIELD_OPERATIONS } from '@kbn/ml-anomaly-utils'; import { MlCoreSetup } from '../plugin'; import { ML_APP_LOCATOR } from '../../common/constants/locator'; import { @@ -18,7 +19,6 @@ import { isSwimLaneEmbeddable, SwimLaneDrilldownContext, } from '../embeddables'; -import { ENTITY_FIELD_OPERATIONS } from '../../common/util/anomaly_utils'; import { ExplorerAppState } from '../../common/types/locator'; export const OPEN_IN_ANOMALY_EXPLORER_ACTION = 'openInAnomalyExplorerAction'; @@ -76,7 +76,7 @@ export function createOpenInExplorerAction( if ( Array.isArray(entityFields) && entityFields.length === 1 && - entityFields[0].operation === ENTITY_FIELD_OPERATIONS.ADD + entityFields[0].operation === ML_ENTITY_FIELD_OPERATIONS.ADD ) { const { fieldName, fieldValue } = entityFields[0]; if (fieldName !== undefined && fieldValue !== undefined) { diff --git a/x-pack/plugins/ml/readme.md b/x-pack/plugins/ml/readme.md index c4b88780f6659..72739cc79fffe 100644 --- a/x-pack/plugins/ml/readme.md +++ b/x-pack/plugins/ml/readme.md @@ -129,7 +129,7 @@ With PATH_TO_CONFIG and other options as follows. The `short tests` group contains tests for page navigation, model management, feature controls, settings and notifications. Test files for each group are located in the directory of their configuration file. - + 1. Functional UI tests with `Basic` license: Group | PATH_TO_CONFIG @@ -165,8 +165,11 @@ and Kibana instance that the tests will be run against. The generated screenshots are stored in `x-pack/test/functional/screenshots/session/ml_docs`. ML screenshot generation tests are located in `x-pack/test/screenshot_creation/apps/ml_docs`. + ## Shared functions +Note: We are in the process of moving shared code to packages, for example `@kbn/ml-anomaly-utils`. We'll update the docs accordingly once this work is done. + You can find the ML shared functions in the following files in GitHub: ``` @@ -186,7 +189,7 @@ import { MlPluginSetup } from '../../../../ml/server'; or ``` -import { ANOMALY_SEVERITY } from '../../ml/common'; +import { ML_ANOMALY_SEVERITY } from '../../ml/common'; ``` Functions are shared from the following directories: diff --git a/x-pack/plugins/ml/server/index.ts b/x-pack/plugins/ml/server/index.ts index 571612f1a4af9..9f46005dd5068 100644 --- a/x-pack/plugins/ml/server/index.ts +++ b/x-pack/plugins/ml/server/index.ts @@ -9,9 +9,6 @@ import { PluginInitializerContext } from '@kbn/core/server'; import { MlServerPlugin } from './plugin'; export type { MlPluginSetup, MlPluginStart } from './plugin'; export type { - AnomalyRecordDoc as MlAnomalyRecordDoc, - AnomaliesTableRecord as MlAnomaliesTableRecord, - AnomalyResultType as MlAnomalyResultType, DatafeedStats as MlDatafeedStats, Job as MlJob, MlSummaryJob, diff --git a/x-pack/plugins/ml/server/lib/alerts/alerting_service.ts b/x-pack/plugins/ml/server/lib/alerts/alerting_service.ts index 36182956b7388..84241f58d04dc 100644 --- a/x-pack/plugins/ml/server/lib/alerts/alerting_service.ts +++ b/x-pack/plugins/ml/server/lib/alerts/alerting_service.ts @@ -16,13 +16,18 @@ import { SerializedFieldFormat, } from '@kbn/field-formats-plugin/common'; import { isDefined } from '@kbn/ml-is-defined'; +import { + getEntityFieldName, + getEntityFieldValue, + type MlAnomalyRecordDoc, + type MlAnomalyResultType, + ML_ANOMALY_RESULT_TYPE, +} from '@kbn/ml-anomaly-utils'; import { MlClient } from '../ml_client'; import { MlAnomalyDetectionAlertParams, MlAnomalyDetectionAlertPreviewRequest, } from '../../routes/schemas/alerting_schema'; -import { ANOMALY_RESULT_TYPE } from '../../../common/constants/anomalies'; -import { AnomalyRecordDoc, AnomalyResultType } from '../../../common/types/anomalies'; import { AlertExecutionResult, InfluencerAnomalyAlertDoc, @@ -35,7 +40,6 @@ import { AnomalyDetectionAlertContext } from './register_anomaly_detection_alert import { resolveMaxTimeInterval } from '../../../common/util/job_utils'; import { getTopNBuckets, resolveLookbackInterval } from '../../../common/util/alerts'; import type { DatafeedsService } from '../../models/job_service/datafeeds'; -import { getEntityFieldName, getEntityFieldValue } from '../../../common/util/anomaly_utils'; import { FieldFormatsRegistryProvider } from '../../../common/types/kibana'; import type { AwaitReturnType } from '../../../common/types/common'; import { getTypicalAndActualValues } from '../../models/results_service/results_service'; @@ -52,7 +56,7 @@ type AggResultsResponse = { key?: number } & { }; interface AnomalyESQueryParams { - resultType: AnomalyResultType; + resultType: MlAnomalyResultType; /** Appropriate score field for requested result type. */ anomalyScoreField: string; anomalyScoreThreshold: number; @@ -73,10 +77,10 @@ const TIME_RANGE_PADDING = 10; export function buildExplorerUrl( jobIds: string[], timeRange: { from: string; to: string; mode?: string }, - type: AnomalyResultType, + type: MlAnomalyResultType, r?: AlertExecutionResult ): string { - const isInfluencerResult = type === ANOMALY_RESULT_TYPE.INFLUENCER; + const isInfluencerResult = type === ML_ANOMALY_RESULT_TYPE.INFLUENCER; /** * Disabled until Anomaly Explorer page is fixed and properly @@ -150,9 +154,9 @@ export function buildExplorerUrl( * Mapping for result types and corresponding score fields. */ const resultTypeScoreMapping = { - [ANOMALY_RESULT_TYPE.BUCKET]: 'anomaly_score', - [ANOMALY_RESULT_TYPE.RECORD]: 'record_score', - [ANOMALY_RESULT_TYPE.INFLUENCER]: 'influencer_score', + [ML_ANOMALY_RESULT_TYPE.BUCKET]: 'anomaly_score', + [ML_ANOMALY_RESULT_TYPE.RECORD]: 'record_score', + [ML_ANOMALY_RESULT_TYPE.INFLUENCER]: 'influencer_score', }; /** @@ -212,7 +216,7 @@ export function alertingServiceProvider( } ); - const getAggResultsLabel = (resultType: AnomalyResultType) => { + const getAggResultsLabel = (resultType: MlAnomalyResultType) => { return { aggGroupLabel: `${resultType}_results` as PreviewResultsKeys, topHitsLabel: `top_${resultType}_hits` as TopHitsResultsKeys, @@ -225,20 +229,20 @@ export function alertingServiceProvider( * @param severity */ const getResultTypeAggRequest = ( - resultType: AnomalyResultType, + resultType: MlAnomalyResultType, severity: number, useInitialScore?: boolean ) => { - const influencerScoreField = getScoreFields(ANOMALY_RESULT_TYPE.INFLUENCER, useInitialScore); - const recordScoreField = getScoreFields(ANOMALY_RESULT_TYPE.RECORD, useInitialScore); - const bucketScoreField = getScoreFields(ANOMALY_RESULT_TYPE.BUCKET, useInitialScore); + const influencerScoreField = getScoreFields(ML_ANOMALY_RESULT_TYPE.INFLUENCER, useInitialScore); + const recordScoreField = getScoreFields(ML_ANOMALY_RESULT_TYPE.RECORD, useInitialScore); + const bucketScoreField = getScoreFields(ML_ANOMALY_RESULT_TYPE.BUCKET, useInitialScore); return { influencer_results: { filter: { range: { [influencerScoreField]: { - gte: resultType === ANOMALY_RESULT_TYPE.INFLUENCER ? severity : 0, + gte: resultType === ML_ANOMALY_RESULT_TYPE.INFLUENCER ? severity : 0, }, }, }, @@ -274,7 +278,7 @@ export function alertingServiceProvider( filter: { range: { [recordScoreField]: { - gte: resultType === ANOMALY_RESULT_TYPE.RECORD ? severity : 0, + gte: resultType === ML_ANOMALY_RESULT_TYPE.RECORD ? severity : 0, }, }, }, @@ -317,7 +321,7 @@ export function alertingServiceProvider( }, }, }, - ...(resultType === ANOMALY_RESULT_TYPE.BUCKET + ...(resultType === ML_ANOMALY_RESULT_TYPE.BUCKET ? { bucket_results: { filter: { @@ -361,15 +365,15 @@ export function alertingServiceProvider( /** * Provides a key for alert instance. */ - const getAlertInstanceKey = (source: AnomalyRecordDoc): string => { + const getAlertInstanceKey = (source: MlAnomalyRecordDoc): string => { return source.job_id; }; - const getScoreFields = (resultType: AnomalyResultType, useInitialScore?: boolean) => { + const getScoreFields = (resultType: MlAnomalyResultType, useInitialScore?: boolean) => { return `${useInitialScore ? 'initial_' : ''}${resultTypeScoreMapping[resultType]}`; }; - const getRecordKey = (source: AnomalyRecordDoc): string => { + const getRecordKey = (source: MlAnomalyRecordDoc): string => { let alertInstanceKey = `${source.job_id}_${source.timestamp}`; const fieldName = getEntityFieldName(source); @@ -387,7 +391,7 @@ export function alertingServiceProvider( * @param resultType */ const getResultsFormatter = ( - resultType: AnomalyResultType, + resultType: MlAnomalyResultType, useInitialScore: boolean = false, formatters: FieldFormatters ) => { @@ -434,7 +438,7 @@ export function alertingServiceProvider( typical: typical?.map((t) => formatter(t)), actual: actual?.map((a) => formatter(a)), score: Math.floor( - h._source[getScoreFields(ANOMALY_RESULT_TYPE.RECORD, useInitialScore)] + h._source[getScoreFields(ML_ANOMALY_RESULT_TYPE.RECORD, useInitialScore)] ), unique_key: getRecordKey(h._source), }; @@ -443,7 +447,7 @@ export function alertingServiceProvider( return { ...h._source, score: Math.floor( - h._source[getScoreFields(ANOMALY_RESULT_TYPE.INFLUENCER, useInitialScore)] + h._source[getScoreFields(ML_ANOMALY_RESULT_TYPE.INFLUENCER, useInitialScore)] ), unique_key: `${h._source.timestamp}_${h._source.influencer_field_name}_${h._source.influencer_field_value}`, }; @@ -518,7 +522,7 @@ export function alertingServiceProvider( }, { terms: { - result_type: Object.values(ANOMALY_RESULT_TYPE) as string[], + result_type: Object.values(ML_ANOMALY_RESULT_TYPE) as string[], }, }, ...(params.includeInterim @@ -673,7 +677,7 @@ export function alertingServiceProvider( }, { terms: { - result_type: Object.values(ANOMALY_RESULT_TYPE) as string[], + result_type: Object.values(ML_ANOMALY_RESULT_TYPE) as string[], }, }, { diff --git a/x-pack/plugins/ml/server/models/annotation_service/annotation.ts b/x-pack/plugins/ml/server/models/annotation_service/annotation.ts index 2501fa93ac258..91bfd177feb21 100644 --- a/x-pack/plugins/ml/server/models/annotation_service/annotation.ts +++ b/x-pack/plugins/ml/server/models/annotation_service/annotation.ts @@ -10,8 +10,8 @@ import { each, get } from 'lodash'; import { IScopedClusterClient } from '@kbn/core/server'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { ML_PARTITION_FIELDS } from '@kbn/ml-anomaly-utils'; import { ANNOTATION_EVENT_USER, ANNOTATION_TYPE } from '../../../common/constants/annotations'; -import { PARTITION_FIELDS } from '../../../common/constants/anomalies'; import { ML_ANNOTATIONS_INDEX_ALIAS_READ, ML_ANNOTATIONS_INDEX_ALIAS_WRITE, @@ -264,7 +264,7 @@ export function annotationProvider({ asInternalUser }: IScopedClusterClient) { // clause to get annotations that have no partition fields const haveAnyPartitionFields: object[] = []; - PARTITION_FIELDS.forEach((field) => { + ML_PARTITION_FIELDS.forEach((field) => { haveAnyPartitionFields.push({ exists: { field: getAnnotationFieldName(field), diff --git a/x-pack/plugins/ml/server/models/results_service/anomaly_charts.ts b/x-pack/plugins/ml/server/models/results_service/anomaly_charts.ts index bcce2455539f7..ea282d78ada60 100644 --- a/x-pack/plugins/ml/server/models/results_service/anomaly_charts.ts +++ b/x-pack/plugins/ml/server/models/results_service/anomaly_charts.ts @@ -12,6 +12,13 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/types'; import { extent, max, min } from 'd3'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import { isDefined } from '@kbn/ml-is-defined'; +import { + aggregationTypeTransform, + getEntityFieldList, + isMultiBucketAnomaly, + type MlEntityField, +} from '@kbn/ml-anomaly-utils'; +import type { MlAnomalyRecordDoc, MlRecordForInfluencer } from '@kbn/ml-anomaly-utils'; import type { MlClient } from '../../lib/ml_client'; import { isRuntimeMappings } from '../../../common'; import type { @@ -34,14 +41,8 @@ import { mlFunctionToESAggregation, } from '../../../common/util/job_utils'; import { CriteriaField } from './results_service'; -import { - aggregationTypeTransform, - EntityField, - getEntityFieldList, - isMultiBucketAnomaly, -} from '../../../common/util/anomaly_utils'; import { InfluencersFilterQuery } from '../../../common/types/es_client'; -import { AnomalyRecordDoc, CombinedJob, Datafeed, RecordForInfluencer } from '../../shared'; +import type { CombinedJob, Datafeed } from '../../shared'; import { ES_AGGREGATION, ML_JOB_AGGREGATION } from '../../../common/constants/aggregation_types'; import { parseInterval } from '../../../common/util/parse_interval'; import { _DOC_COUNT, DOC_COUNT } from '../../../common/constants/field_types'; @@ -129,7 +130,7 @@ export function anomalyChartsDataProvider(mlClient: MlClient, client: IScopedClu async function fetchMetricData( index: string, - entityFields: EntityField[], + entityFields: MlEntityField[], query: object | undefined, metricFunction: string | null, // ES aggregation name metricFieldName: string | undefined, @@ -453,7 +454,7 @@ export function anomalyChartsDataProvider(mlClient: MlClient, client: IScopedClu function processRecordsForDisplay( combinedJobRecords: Record, - anomalyRecords: RecordForInfluencer[] + anomalyRecords: MlRecordForInfluencer[] ): { records: ChartRecord[]; errors: Record> | undefined } { // Aggregate the anomaly data by detector, and entity (by/over/partition). if (anomalyRecords.length === 0) { @@ -1131,7 +1132,7 @@ export function anomalyChartsDataProvider(mlClient: MlClient, client: IScopedClu function getChartDataForPointSearch( chartData: ChartPoint[], - record: AnomalyRecordDoc, + record: MlAnomalyRecordDoc, chartType: ChartType ) { if ( @@ -1439,8 +1440,8 @@ export function anomalyChartsDataProvider(mlClient: MlClient, client: IScopedClu async function getEventDistributionData( index: string, - splitField: EntityField | undefined | null, - filterField: EntityField | undefined | null, + splitField: MlEntityField | undefined | null, + filterField: MlEntityField | undefined | null, query: any, metricFunction: string | undefined | null, // ES aggregation name metricFieldName: string | undefined, @@ -1645,7 +1646,7 @@ export function anomalyChartsDataProvider(mlClient: MlClient, client: IScopedClu } async function getRecordsForCriteriaChart(config: SeriesConfigWithMetadata, range: ChartRange) { - let criteria: EntityField[] = []; + let criteria: MlEntityField[] = []; criteria.push({ fieldName: 'detector_index', fieldValue: config.detectorIndex }); criteria = criteria.concat(config.entityFields); @@ -1812,13 +1813,13 @@ export function anomalyChartsDataProvider(mlClient: MlClient, client: IScopedClu async function getRecordsForInfluencer( jobIds: string[], - influencers: EntityField[], + influencers: MlEntityField[], threshold: number, earliestMs: number, latestMs: number, maxResults: number, influencersFilterQuery?: InfluencersFilterQuery - ): Promise { + ): Promise { // Build the criteria to use in the bool filter part of the request. // Add criteria for the time range, record score, plus any specified job IDs. const boolCriteria: estypes.QueryDslBoolQuery['must'] = [ @@ -1894,7 +1895,7 @@ export function anomalyChartsDataProvider(mlClient: MlClient, client: IScopedClu }); } - const response = await mlClient.anomalySearch>( + const response = await mlClient.anomalySearch>( { body: { size: maxResults !== undefined ? maxResults : 100, @@ -1933,7 +1934,7 @@ export function anomalyChartsDataProvider(mlClient: MlClient, client: IScopedClu */ async function getAnomalyChartsData(options: { jobIds: string[]; - influencers: EntityField[]; + influencers: MlEntityField[]; threshold: number; earliestMs: number; latestMs: number; diff --git a/x-pack/plugins/ml/server/models/results_service/build_anomaly_table_items.d.ts b/x-pack/plugins/ml/server/models/results_service/build_anomaly_table_items.d.ts index 00f6814fc23f2..d9ff1fc959251 100644 --- a/x-pack/plugins/ml/server/models/results_service/build_anomaly_table_items.d.ts +++ b/x-pack/plugins/ml/server/models/results_service/build_anomaly_table_items.d.ts @@ -5,10 +5,10 @@ * 2.0. */ -import { AnomaliesTableRecord } from '../../../common/types/anomalies'; +import type { MlAnomaliesTableRecord } from '@kbn/ml-anomaly-utils'; export function buildAnomalyTableItems( anomalyRecords: any, aggregationInterval: any, dateFormatTz: string -): AnomaliesTableRecord[]; +): MlAnomaliesTableRecord[]; diff --git a/x-pack/plugins/ml/server/models/results_service/build_anomaly_table_items.js b/x-pack/plugins/ml/server/models/results_service/build_anomaly_table_items.js index 380d5e4a881aa..306f04c7ca95c 100644 --- a/x-pack/plugins/ml/server/models/results_service/build_anomaly_table_items.js +++ b/x-pack/plugins/ml/server/models/results_service/build_anomaly_table_items.js @@ -13,7 +13,7 @@ import { getEntityFieldValue, showActualForFunction, showTypicalForFunction, -} from '../../../common/util/anomaly_utils'; +} from '@kbn/ml-anomaly-utils'; // Builds the items for display in the anomalies table from the supplied list of anomaly records. // Provide the timezone to use for aggregating anomalies (by day or hour) as set in the diff --git a/x-pack/plugins/ml/server/models/results_service/get_partition_fields_values.ts b/x-pack/plugins/ml/server/models/results_service/get_partition_fields_values.ts index 8b6c3c4db603a..40479ce3c618c 100644 --- a/x-pack/plugins/ml/server/models/results_service/get_partition_fields_values.ts +++ b/x-pack/plugins/ml/server/models/results_service/get_partition_fields_values.ts @@ -7,15 +7,14 @@ import Boom from '@hapi/boom'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { PARTITION_FIELDS } from '../../../common/constants/anomalies'; -import { PartitionFieldsType } from '../../../common/types/anomalies'; +import { type MlPartitionFieldsType, ML_PARTITION_FIELDS } from '@kbn/ml-anomaly-utils'; import { CriteriaField } from './results_service'; import { FieldConfig, FieldsConfig } from '../../routes/schemas/results_service_schema'; import type { MlClient } from '../../lib/ml_client'; type SearchTerm = | { - [key in PartitionFieldsType]?: string; + [key in MlPartitionFieldsType]?: string; } | undefined; @@ -33,7 +32,7 @@ export interface PartitionFieldData { * @returns {Object} */ function getFieldAgg( - fieldType: PartitionFieldsType, + fieldType: MlPartitionFieldsType, isModelPlotSearch: boolean, query?: string, fieldConfig?: FieldConfig @@ -117,9 +116,9 @@ function getFieldAgg( * @param aggs - Aggregation response */ function getFieldObject( - fieldType: PartitionFieldsType, + fieldType: MlPartitionFieldsType, aggs: Record -): Record | {} { +): Record | {} { const fieldNameKey = `${fieldType}_name` as const; const fieldValueKey = `${fieldType}_value` as const; @@ -146,7 +145,7 @@ function getFieldObject( : {}; } -export type PartitionFieldValueResponse = Record; +export type PartitionFieldValueResponse = Record; export const getPartitionFieldsValuesFactory = (mlClient: MlClient) => /** @@ -231,7 +230,7 @@ export const getPartitionFieldsValuesFactory = (mlClient: MlClient) => }, }, aggs: { - ...PARTITION_FIELDS.reduce((acc, key) => { + ...ML_PARTITION_FIELDS.reduce((acc, key) => { return { ...acc, ...getFieldAgg(key, isModelPlotSearch, searchTerm[key], fieldsConfig[key]), @@ -250,7 +249,7 @@ export const getPartitionFieldsValuesFactory = (mlClient: MlClient) => [jobId] ); - return PARTITION_FIELDS.reduce((acc, key) => { + return ML_PARTITION_FIELDS.reduce((acc, key) => { return { ...acc, ...getFieldObject(key, body.aggregations!), diff --git a/x-pack/plugins/ml/server/models/results_service/results_service.ts b/x-pack/plugins/ml/server/models/results_service/results_service.ts index deb222ef6b3bd..30ce44adca152 100644 --- a/x-pack/plugins/ml/server/models/results_service/results_service.ts +++ b/x-pack/plugins/ml/server/models/results_service/results_service.ts @@ -10,15 +10,18 @@ import { sortBy, slice, get, cloneDeep } from 'lodash'; import moment from 'moment'; import Boom from '@hapi/boom'; import { IScopedClusterClient } from '@kbn/core/server'; +import { + showActualForFunction, + showTypicalForFunction, + type MlAnomaliesTableRecord, + type MlAnomalyCategorizerStatsDoc, + type MlAnomalyRecordDoc, + ML_JOB_ID, + ML_PARTITION_FIELD_VALUE, +} from '@kbn/ml-anomaly-utils'; import { buildAnomalyTableItems } from './build_anomaly_table_items'; import { ANOMALIES_TABLE_DEFAULT_QUERY_SIZE } from '../../../common/constants/search'; import { getPartitionFieldsValuesFactory } from './get_partition_fields_values'; -import { - AnomaliesTableRecord, - AnomalyCategorizerStatsDoc, - AnomalyRecordDoc, -} from '../../../common/types/anomalies'; -import { JOB_ID, PARTITION_FIELD_VALUE } from '../../../common/constants/anomalies'; import { GetStoppedPartitionResult, GetDatafeedResultsChartDataResult, @@ -28,7 +31,6 @@ import { import type { MlClient } from '../../lib/ml_client'; import { datafeedsProvider } from '../job_service/datafeeds'; import { annotationServiceProvider } from '../annotation_service'; -import { showActualForFunction, showTypicalForFunction } from '../../../common/util/anomaly_utils'; import { anomalyChartsDataProvider } from './anomaly_charts'; // Service for carrying out Elasticsearch queries to obtain data for the @@ -51,7 +53,7 @@ interface Influencer { * Extracts typical and actual values from the anomaly record. * @param source */ -export function getTypicalAndActualValues(source: AnomalyRecordDoc) { +export function getTypicalAndActualValues(source: MlAnomalyRecordDoc) { const result: { actual?: number[]; typical?: number[] } = {}; const functionDescription = source.function_description || ''; @@ -219,7 +221,7 @@ export function resultsServiceProvider(mlClient: MlClient, client?: IScopedClust ); const tableData: { - anomalies: AnomaliesTableRecord[]; + anomalies: MlAnomaliesTableRecord[]; interval: string; examplesByJobId?: { [key: string]: any }; } = { @@ -228,7 +230,7 @@ export function resultsServiceProvider(mlClient: MlClient, client?: IScopedClust }; if ((body.hits.total as estypes.SearchTotalHits).value > 0) { - let records: AnomalyRecordDoc[] = []; + let records: MlAnomalyRecordDoc[] = []; body.hits.hits.forEach((hit: any) => { records.push(hit._source); }); @@ -510,7 +512,7 @@ export function resultsServiceProvider(mlClient: MlClient, client?: IScopedClust }, }); } - const body = await mlClient.anomalySearch( + const body = await mlClient.anomalySearch( { body: { query: { @@ -534,7 +536,7 @@ export function resultsServiceProvider(mlClient: MlClient, client?: IScopedClust async function getCategoryStoppedPartitions( jobIds: string[], - fieldToBucket: typeof JOB_ID | typeof PARTITION_FIELD_VALUE = PARTITION_FIELD_VALUE + fieldToBucket: typeof ML_JOB_ID | typeof ML_PARTITION_FIELD_VALUE = ML_PARTITION_FIELD_VALUE ): Promise { let finalResults: GetStoppedPartitionResult = { jobs: {}, @@ -557,12 +559,12 @@ export function resultsServiceProvider(mlClient: MlClient, client?: IScopedClust .map((j) => j.job_id); let aggs: any; - if (fieldToBucket === JOB_ID) { + if (fieldToBucket === ML_JOB_ID) { // if bucketing by job_id, then return list of job_ids with at least one stopped_partitions aggs = { unique_terms: { terms: { - field: JOB_ID, + field: ML_JOB_ID, }, }, }; @@ -571,12 +573,12 @@ export function resultsServiceProvider(mlClient: MlClient, client?: IScopedClust aggs = { jobs: { terms: { - field: JOB_ID, + field: ML_JOB_ID, }, aggs: { unique_stopped_partitions: { terms: { - field: PARTITION_FIELD_VALUE, + field: ML_PARTITION_FIELD_VALUE, }, }, }, @@ -620,14 +622,14 @@ export function resultsServiceProvider(mlClient: MlClient, client?: IScopedClust }, jobIds ); - if (fieldToBucket === JOB_ID) { + if (fieldToBucket === ML_JOB_ID) { finalResults = { // @ts-expect-error incorrect search response type jobs: results.aggregations?.unique_terms?.buckets.map( (b: { key: string; doc_count: number }) => b.key ), }; - } else if (fieldToBucket === PARTITION_FIELD_VALUE) { + } else if (fieldToBucket === ML_PARTITION_FIELD_VALUE) { const jobs: Record = jobIdsWithStopOnWarnSet.reduce( (obj: Record, jobId: string) => { obj[jobId] = []; diff --git a/x-pack/plugins/ml/server/routes/schemas/alerting_schema.ts b/x-pack/plugins/ml/server/routes/schemas/alerting_schema.ts index ed58d8d5c9313..1ca1db8811a3a 100644 --- a/x-pack/plugins/ml/server/routes/schemas/alerting_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/alerting_schema.ts @@ -7,8 +7,8 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; +import { ML_ANOMALY_RESULT_TYPE } from '@kbn/ml-anomaly-utils'; import { ALERT_PREVIEW_SAMPLE_SIZE } from '../../../common/constants/alerts'; -import { ANOMALY_RESULT_TYPE } from '../../../common/constants/anomalies'; const jobsSelectionSchema = schema.object( { @@ -32,9 +32,9 @@ export const mlAnomalyDetectionAlertParams = schema.object({ severity: schema.number({ min: 0, max: 100 }), /** Result type to alert upon */ resultType: schema.oneOf([ - schema.literal(ANOMALY_RESULT_TYPE.RECORD), - schema.literal(ANOMALY_RESULT_TYPE.BUCKET), - schema.literal(ANOMALY_RESULT_TYPE.INFLUENCER), + schema.literal(ML_ANOMALY_RESULT_TYPE.RECORD), + schema.literal(ML_ANOMALY_RESULT_TYPE.BUCKET), + schema.literal(ML_ANOMALY_RESULT_TYPE.INFLUENCER), ]), includeInterim: schema.boolean({ defaultValue: true }), /** User's override for the lookback interval */ diff --git a/x-pack/plugins/ml/server/shared.ts b/x-pack/plugins/ml/server/shared.ts index bd0807a1e30d8..3a916f613ad32 100644 --- a/x-pack/plugins/ml/server/shared.ts +++ b/x-pack/plugins/ml/server/shared.ts @@ -5,7 +5,6 @@ * 2.0. */ -export * from '../common/types/anomalies'; export * from '../common/types/anomaly_detection_jobs'; export * from './lib/capabilities/errors'; export type { ModuleSetupPayload } from './shared_services/providers/modules'; diff --git a/x-pack/plugins/ml/server/usage/collector.ts b/x-pack/plugins/ml/server/usage/collector.ts index 876e604fcf6f2..455675c46ba3f 100644 --- a/x-pack/plugins/ml/server/usage/collector.ts +++ b/x-pack/plugins/ml/server/usage/collector.ts @@ -6,8 +6,8 @@ */ import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; +import { MlAnomalyResultType } from '@kbn/ml-anomaly-utils'; import { ML_ALERT_TYPES } from '../../common/constants/alerts'; -import { AnomalyResultType } from '../../common/types/anomalies'; import { MlAnomalyDetectionJobsHealthRuleParams } from '../../common/types/alerts'; import { getResultJobsHealthRuleConfig } from '../../common/util/alerts'; @@ -125,7 +125,7 @@ export function registerCollector( const aggResponse = result.aggregations as { count_by_result_type: { buckets: Array<{ - key: AnomalyResultType; + key: MlAnomalyResultType; doc_count: number; }>; }; diff --git a/x-pack/plugins/ml/tsconfig.json b/x-pack/plugins/ml/tsconfig.json index 8288c9998fc7d..3f5d9a8111023 100644 --- a/x-pack/plugins/ml/tsconfig.json +++ b/x-pack/plugins/ml/tsconfig.json @@ -90,5 +90,6 @@ "@kbn/usage-collection-plugin", "@kbn/utility-types", "@kbn/ml-error-utils", + "@kbn/ml-anomaly-utils", ], } diff --git a/x-pack/plugins/observability/public/components/alerts_flyout_body.tsx b/x-pack/plugins/observability/public/components/alerts_flyout_body.tsx index 8f1b290b439d2..7686144affbb8 100644 --- a/x-pack/plugins/observability/public/components/alerts_flyout_body.tsx +++ b/x-pack/plugins/observability/public/components/alerts_flyout_body.tsx @@ -30,7 +30,7 @@ import { AlertLifecycleStatusBadge } from '@kbn/alerts-ui-shared'; import moment from 'moment-timezone'; import { useUiSetting } from '@kbn/kibana-react-plugin/public'; import { useKibana } from '../utils/kibana_react'; -import { asDuration } from '../../common/utils/formatters'; +import { asDuration, toMicroseconds } from '../../common/utils/formatters'; import { paths } from '../config/paths'; import { translations } from '../config/translations'; import { formatAlertEvaluationValue } from '../utils/format_alert_evaluation_value'; @@ -42,6 +42,14 @@ interface FlyoutProps { id?: string; } +// For APM Latency threshold rule, threshold is in ms but the duration formatter works with microseconds +const normalizeUnit = (ruleTypeId: string, value?: number) => { + if (ruleTypeId === 'apm.transaction_duration' && value) { + return toMicroseconds(value, 'milliseconds'); + } + return value; +}; + export function AlertsFlyoutBody({ alert, id: pageId }: FlyoutProps) { const { http: { @@ -89,7 +97,7 @@ export function AlertsFlyoutBody({ alert, id: pageId }: FlyoutProps) { title: translations.alertsFlyout.expectedValueLabel, description: formatAlertEvaluationValue( alert.fields[ALERT_RULE_TYPE_ID], - alert.fields[ALERT_EVALUATION_THRESHOLD] + normalizeUnit(alert.fields[ALERT_RULE_TYPE_ID], alert.fields[ALERT_EVALUATION_THRESHOLD]) ), }, { diff --git a/x-pack/plugins/observability/public/components/shared/alert_search_bar/alert_search_bar.tsx b/x-pack/plugins/observability/public/components/shared/alert_search_bar/alert_search_bar.tsx index 6abf6017cc0c2..1442dbb9dbf91 100644 --- a/x-pack/plugins/observability/public/components/shared/alert_search_bar/alert_search_bar.tsx +++ b/x-pack/plugins/observability/public/components/shared/alert_search_bar/alert_search_bar.tsx @@ -111,7 +111,7 @@ export function ObservabilityAlertSearchBar({ rangeFrom={rangeFrom} rangeTo={rangeTo} query={kuery} - onQueryChange={onSearchBarParamsChange} + onQuerySubmit={onSearchBarParamsChange} /> diff --git a/x-pack/plugins/observability/public/hooks/slo/use_clone_slo.ts b/x-pack/plugins/observability/public/hooks/slo/use_clone_slo.ts index 00fddd8aebf14..3c32ecb227a35 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_clone_slo.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_clone_slo.ts @@ -33,7 +33,7 @@ export function useCloneSlo() { { onMutate: async ({ slo, idToCopyFrom }) => { // Cancel any outgoing refetches (so they don't overwrite our optimistic update) - await queryClient.cancelQueries(['fetchSloList']); + await queryClient.cancelQueries(['fetchSloList'], { exact: false }); const latestFetchSloListRequest = ( queryClient.getQueriesData(['fetchSloList']) || [] @@ -80,7 +80,7 @@ export function useCloneSlo() { ); }, onSuccess: () => { - queryClient.invalidateQueries(['fetchSloList']); + queryClient.invalidateQueries(['fetchSloList'], { exact: false }); }, } ); diff --git a/x-pack/plugins/observability/public/hooks/slo/use_delete_slo.ts b/x-pack/plugins/observability/public/hooks/slo/use_delete_slo.ts index 2878b3e158684..a3829cbebf2dc 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_delete_slo.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_delete_slo.ts @@ -34,7 +34,7 @@ export function useDeleteSlo() { { onMutate: async (slo) => { // Cancel any outgoing refetches (so they don't overwrite our optimistic update) - await queryClient.cancelQueries(['fetchSloList']); + await queryClient.cancelQueries(['fetchSloList'], { exact: false }); const latestFetchSloListRequest = ( queryClient.getQueriesData(['fetchSloList']) || [] @@ -76,8 +76,13 @@ export function useDeleteSlo() { }) ); }, - onSuccess: (_success, slo) => { - queryClient.invalidateQueries(['fetchSloList']); + onSuccess: () => { + if ( + queryClient.getQueryCache().find(['fetchSloList'], { exact: false })?.options // @ts-ignore + .refetchInterval === undefined + ) { + queryClient.invalidateQueries(['fetchSloList'], { exact: false }); + } }, } ); diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_historical_summary.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_historical_summary.ts index fe9ffd92f579d..0eb59aa85bc83 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_fetch_historical_summary.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_historical_summary.ts @@ -50,6 +50,7 @@ export function useFetchHistoricalSummary({ }, refetchInterval: shouldRefetch ? LONG_REFETCH_INTERVAL : undefined, refetchOnWindowFocus: false, + keepPreviousData: true, }); return { diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_list.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_list.ts index 086ec0d8e77a9..990b303ea0342 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_list.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_list.ts @@ -84,7 +84,7 @@ export function useFetchSloList({ return; } - if (results.find((slo) => slo.summary.status === 'NO_DATA')) { + if (results.find((slo) => slo.summary.status === 'NO_DATA' || !slo.summary)) { setStateRefetchInterval(SHORT_REFETCH_INTERVAL); } else { setStateRefetchInterval(LONG_REFETCH_INTERVAL); diff --git a/x-pack/plugins/observability/public/pages/alert_details/components/page_title.test.tsx b/x-pack/plugins/observability/public/pages/alert_details/components/page_title.test.tsx index d988be160c48a..125ea4885a564 100644 --- a/x-pack/plugins/observability/public/pages/alert_details/components/page_title.test.tsx +++ b/x-pack/plugins/observability/public/pages/alert_details/components/page_title.test.tsx @@ -28,9 +28,7 @@ describe('Page Title', () => { it('should display Log threshold title', () => { const { getByTestId } = renderComp(defaultProps); - expect(getByTestId('page-title-container').children.item(0)?.textContent).toEqual( - 'Log threshold breached' - ); + expect(getByTestId('page-title-container').textContent).toContain('Log threshold breached'); }); it('should display Anomaly title', () => { @@ -46,9 +44,7 @@ describe('Page Title', () => { const { getByTestId } = renderComp(props); - expect(getByTestId('page-title-container').children.item(0)?.textContent).toEqual( - 'Anomaly detected' - ); + expect(getByTestId('page-title-container').textContent).toContain('Anomaly detected'); }); it('should display Inventory title', () => { @@ -64,7 +60,7 @@ describe('Page Title', () => { const { getByTestId } = renderComp(props); - expect(getByTestId('page-title-container').children.item(0)?.textContent).toEqual( + expect(getByTestId('page-title-container').textContent).toContain( 'Inventory threshold breached' ); }); diff --git a/x-pack/plugins/observability/public/pages/alert_details/components/page_title.tsx b/x-pack/plugins/observability/public/pages/alert_details/components/page_title.tsx index 201e19a5c94a6..a2e47ac4ccf16 100644 --- a/x-pack/plugins/observability/public/pages/alert_details/components/page_title.tsx +++ b/x-pack/plugins/observability/public/pages/alert_details/components/page_title.tsx @@ -35,15 +35,13 @@ export interface PageTitleProps { } export function pageTitleContent(ruleCategory: string) { - return ( - - ); + return i18n.translate('xpack.observability.pages.alertDetails.pageTitle.title', { + defaultMessage: + '{ruleCategory} {ruleCategory, select, Anomaly {detected} Inventory {threshold breached} other {breached}}', + values: { + ruleCategory, + }, + }); } export function PageTitle({ alert }: PageTitleProps) { diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx index eb3a29ea61958..4502e7a2d9d20 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx @@ -181,8 +181,9 @@ export function SloListItem({ button={ ; + return ; } return ( diff --git a/x-pack/plugins/observability/server/plugin.ts b/x-pack/plugins/observability/server/plugin.ts index 9fdf113cc64af..4e44593dc7767 100644 --- a/x-pack/plugins/observability/server/plugin.ts +++ b/x-pack/plugins/observability/server/plugin.ts @@ -14,7 +14,7 @@ import { Logger, } from '@kbn/core/server'; import { hiddenTypes as filesSavedObjectTypes } from '@kbn/files-plugin/server/saved_objects'; -import { PluginSetupContract } from '@kbn/alerting-plugin/server'; +import { PluginSetupContract, PluginStartContract } from '@kbn/alerting-plugin/server'; import { Dataset, RuleRegistryPluginSetupContract } from '@kbn/rule-registry-plugin/server'; import { PluginSetupContract as FeaturesSetup } from '@kbn/features-plugin/server'; import { legacyExperimentalFieldMap } from '@kbn/alerts-as-data-utils'; @@ -59,6 +59,10 @@ interface PluginSetup { usageCollection?: UsageCollectionSetup; } +interface PluginStart { + alerting: PluginStartContract; +} + export class ObservabilityPlugin implements Plugin { private logger: Logger; @@ -67,7 +71,7 @@ export class ObservabilityPlugin implements Plugin { this.logger = initContext.logger.get(); } - public setup(core: CoreSetup, plugins: PluginSetup) { + public setup(core: CoreSetup, plugins: PluginSetup) { const casesCapabilities = createCasesUICapabilities(); const casesApiTags = getCasesApiTags(observabilityFeatureId); @@ -237,13 +241,16 @@ export class ObservabilityPlugin implements Plugin { registerRuleTypes(plugins.alerting, this.logger, ruleDataClient, core.http.basePath); registerSloUsageCollector(plugins.usageCollection); - registerRoutes({ - core, - dependencies: { - ruleDataService, - }, - logger: this.logger, - repository: getObservabilityServerRouteRepository(), + core.getStartServices().then(([coreStart, pluginStart]) => { + registerRoutes({ + core, + dependencies: { + ruleDataService, + getRulesClientWithRequest: pluginStart.alerting.getRulesClientWithRequest, + }, + logger: this.logger, + repository: getObservabilityServerRouteRepository(), + }); }); /** diff --git a/x-pack/plugins/observability/server/routes/register_routes.ts b/x-pack/plugins/observability/server/routes/register_routes.ts index de46f624addb7..f79027bd4d6bb 100644 --- a/x-pack/plugins/observability/server/routes/register_routes.ts +++ b/x-pack/plugins/observability/server/routes/register_routes.ts @@ -10,10 +10,11 @@ import { parseEndpoint, routeValidationObject, } from '@kbn/server-route-repository'; -import { CoreSetup, Logger, RouteRegistrar } from '@kbn/core/server'; +import { CoreSetup, KibanaRequest, Logger, RouteRegistrar } from '@kbn/core/server'; import Boom from '@hapi/boom'; import { errors } from '@elastic/elasticsearch'; import { RuleDataPluginService } from '@kbn/rule-registry-plugin/server'; +import { RulesClientApi } from '@kbn/alerting-plugin/server/types'; import { ObservabilityRequestHandlerContext } from '../types'; import { AbstractObservabilityServerRouteRepository } from './types'; @@ -28,6 +29,7 @@ interface RegisterRoutes { export interface RegisterRoutesDependencies { ruleDataService: RuleDataPluginService; + getRulesClientWithRequest: (request: KibanaRequest) => RulesClientApi; } export function registerRoutes({ repository, core, logger, dependencies }: RegisterRoutes) { diff --git a/x-pack/plugins/observability/server/routes/slo/route.ts b/x-pack/plugins/observability/server/routes/slo/route.ts index 1aec94e3dde1f..0d0a964408773 100644 --- a/x-pack/plugins/observability/server/routes/slo/route.ts +++ b/x-pack/plugins/observability/server/routes/slo/route.ts @@ -106,18 +106,25 @@ const deleteSLORoute = createObservabilityServerRoute({ tags: ['access:slo_write'], }, params: deleteSLOParamsSchema, - handler: async ({ context, params, logger }) => { + handler: async ({ + request, + context, + params, + logger, + dependencies: { getRulesClientWithRequest }, + }) => { if (!isLicenseAtLeastPlatinum(context)) { throw badRequest('Platinum license or higher is needed to make use of this feature.'); } const esClient = (await context.core).elasticsearch.client.asCurrentUser; const soClient = (await context.core).savedObjects.client; + const rulesClient = getRulesClientWithRequest(request); const repository = new KibanaSavedObjectsSLORepository(soClient); const transformManager = new DefaultTransformManager(transformGenerators, esClient, logger); - const deleteSLO = new DeleteSLO(repository, transformManager, esClient); + const deleteSLO = new DeleteSLO(repository, transformManager, esClient, rulesClient); await deleteSLO.execute(params.path.id); }, diff --git a/x-pack/plugins/observability/server/services/slo/delete_slo.test.ts b/x-pack/plugins/observability/server/services/slo/delete_slo.test.ts index a979265ce1ef9..e1e76fa56400d 100644 --- a/x-pack/plugins/observability/server/services/slo/delete_slo.test.ts +++ b/x-pack/plugins/observability/server/services/slo/delete_slo.test.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { RulesClientApi } from '@kbn/alerting-plugin/server/types'; +import { rulesClientMock } from '@kbn/alerting-plugin/server/rules_client.mock'; import { ElasticsearchClient } from '@kbn/core/server'; import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; import { getSLOTransformId, SLO_INDEX_TEMPLATE_NAME } from '../../assets/constants'; @@ -18,17 +20,19 @@ describe('DeleteSLO', () => { let mockRepository: jest.Mocked; let mockTransformManager: jest.Mocked; let mockEsClient: jest.Mocked; + let mockRulesClient: jest.Mocked; let deleteSLO: DeleteSLO; beforeEach(() => { mockRepository = createSLORepositoryMock(); mockTransformManager = createTransformManagerMock(); mockEsClient = elasticsearchServiceMock.createElasticsearchClient(); - deleteSLO = new DeleteSLO(mockRepository, mockTransformManager, mockEsClient); + mockRulesClient = rulesClientMock.create(); + deleteSLO = new DeleteSLO(mockRepository, mockTransformManager, mockEsClient, mockRulesClient); }); describe('happy path', () => { - it('removes the transform, the roll up data and the SLO from the repository', async () => { + it('removes the transform, the roll up data, the associated rules and the SLO from the repository', async () => { const slo = createSLO({ indicator: createAPMTransactionErrorRateIndicator() }); mockRepository.findById.mockResolvedValueOnce(slo); @@ -51,6 +55,9 @@ describe('DeleteSLO', () => { }, }) ); + expect(mockRulesClient.bulkDeleteRules).toHaveBeenCalledWith({ + filter: `alert.attributes.params.sloId:${slo.id}`, + }); expect(mockRepository.deleteById).toHaveBeenCalledWith(slo.id); }); }); diff --git a/x-pack/plugins/observability/server/services/slo/delete_slo.ts b/x-pack/plugins/observability/server/services/slo/delete_slo.ts index 8ec8d2060f730..dc812b1bd4f31 100644 --- a/x-pack/plugins/observability/server/services/slo/delete_slo.ts +++ b/x-pack/plugins/observability/server/services/slo/delete_slo.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { RulesClientApi } from '@kbn/alerting-plugin/server/types'; import { ElasticsearchClient } from '@kbn/core/server'; import { getSLOTransformId, SLO_INDEX_TEMPLATE_NAME } from '../../assets/constants'; @@ -15,7 +16,8 @@ export class DeleteSLO { constructor( private repository: SLORepository, private transformManager: TransformManager, - private esClient: ElasticsearchClient + private esClient: ElasticsearchClient, + private rulesClient: RulesClientApi ) {} public async execute(sloId: string): Promise { @@ -26,6 +28,7 @@ export class DeleteSLO { await this.transformManager.uninstall(sloTransformId); await this.deleteRollupData(slo.id); + await this.deleteAssociatedRules(slo.id); await this.repository.deleteById(slo.id); } @@ -40,4 +43,14 @@ export class DeleteSLO { }, }); } + + private async deleteAssociatedRules(sloId: string): Promise { + try { + await this.rulesClient.bulkDeleteRules({ + filter: `alert.attributes.params.sloId:${sloId}`, + }); + } catch (err) { + // no-op: bulkDeleteRules throws if no rules are found. + } + } } diff --git a/x-pack/plugins/osquery/cypress/e2e/all/add_integration.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/add_integration.cy.ts index e35cc40bbc749..552c3ae1e8c90 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/add_integration.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/add_integration.cy.ts @@ -183,15 +183,11 @@ describe('ALL - Add Integration', () => { cy.get(`[title="${policyName}"]`).click(); cy.getBySel('PackagePoliciesTableUpgradeButton').click(); cy.contains(/^Advanced$/).click(); - cy.getBySel('codeEditorContainer').within(() => { - cy.contains(`"${packName}":`); - }); + cy.get('.kibanaCodeEditor').should('contain', `"${packName}":`); cy.getBySel('saveIntegration').click(); cy.get(`a[title="${integrationName}"]`).click(); cy.contains(/^Advanced$/).click(); - cy.getBySel('codeEditorContainer').within(() => { - cy.contains(`"${packName}":`); - }); + cy.get('.kibanaCodeEditor').should('contain', `"${packName}":`); cy.contains('Cancel').click(); closeModalIfVisible(); cy.get(`[title="${integrationName}"]`) diff --git a/x-pack/plugins/osquery/cypress/e2e/all/alerts.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/alerts.cy.ts index e988cd36425d3..ec0c8c65b7faf 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/alerts.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/alerts.cy.ts @@ -122,8 +122,10 @@ describe('Alert Event Details', () => { it('adds response actions with osquery with proper validation and form values', () => { cy.visit('/app/security/rules'); cy.contains(ruleName).click(); - cy.getBySel('editRuleSettingsLink').click({ force: true }); - cy.getBySel('edit-rule-actions-tab').wait(500).click(); + cy.getBySel('editRuleSettingsLink').click(); + cy.getBySel('globalLoadingIndicator').should('exist'); + cy.getBySel('globalLoadingIndicator').should('not.exist'); + cy.getBySel('edit-rule-actions-tab').click(); cy.contains('Response actions are run on each rule execution'); cy.getBySel(OSQUERY_RESPONSE_ACTION_ADD_BUTTON).click(); cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { @@ -160,8 +162,10 @@ describe('Alert Event Details', () => { cy.contains(`${ruleName} was saved`).should('exist'); closeToastIfVisible(); - cy.getBySel('editRuleSettingsLink').click({ force: true }); - cy.getBySel('edit-rule-actions-tab').wait(500).click(); + cy.getBySel('editRuleSettingsLink').click(); + cy.getBySel('globalLoadingIndicator').should('exist'); + cy.getBySel('globalLoadingIndicator').should('not.exist'); + cy.getBySel('edit-rule-actions-tab').click(); cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { cy.contains('select * from uptime1'); }); @@ -204,8 +208,10 @@ describe('Alert Event Details', () => { cy.contains(`${ruleName} was saved`).should('exist'); closeToastIfVisible(); - cy.getBySel('editRuleSettingsLink').click({ force: true }); - cy.getBySel('edit-rule-actions-tab').wait(500).click(); + cy.getBySel('editRuleSettingsLink').click(); + cy.getBySel('globalLoadingIndicator').should('exist'); + cy.getBySel('globalLoadingIndicator').should('not.exist'); + cy.getBySel('edit-rule-actions-tab').click(); cy.getBySel(RESPONSE_ACTIONS_ITEM_0).within(() => { cy.contains(packName); cy.getBySel('comboBoxInput').type(`${multiQueryPackName}{downArrow}{enter}`); @@ -268,8 +274,10 @@ describe('Alert Event Details', () => { 'You have queries in the investigation guide. Add them as response actions?'; cy.visit('/app/security/rules'); cy.contains(ruleName).click(); - cy.getBySel('editRuleSettingsLink').click({ force: true }); - cy.getBySel('edit-rule-actions-tab').wait(500).click(); + cy.getBySel('editRuleSettingsLink').click(); + cy.getBySel('globalLoadingIndicator').should('exist'); + cy.getBySel('globalLoadingIndicator').should('not.exist'); + cy.getBySel('edit-rule-actions-tab').click(); cy.contains(investigationGuideNote); cy.getBySel('osqueryAddInvestigationGuideQueries').click(); @@ -305,9 +313,9 @@ describe('Alert Event Details', () => { it('should be able to run live query and add to timeline (-depending on the previous test)', () => { const TIMELINE_NAME = 'Untitled timeline'; loadRuleAlerts(ruleName); - cy.getBySel('timeline-context-menu-button').first().click({ force: true }); + cy.getBySel('timeline-context-menu-button').first().click(); cy.contains('Run Osquery'); - cy.getBySel('expand-event').first().click({ force: true }); + cy.getBySel('expand-event').first().click(); cy.getBySel('take-action-dropdown-btn').click(); cy.getBySel('osquery-action-item').click(); cy.contains('1 agent selected.'); @@ -349,7 +357,7 @@ describe('Alert Event Details', () => { it('should substitute parameters in investigation guide', () => { loadRuleAlerts(ruleName); - cy.getBySel('expand-event').first().click({ force: true }); + cy.getBySel('expand-event').first().click(); cy.contains('Get processes').click(); cy.getBySel('flyout-body-osquery').within(() => { cy.contains("SELECT * FROM os_version where name='Ubuntu';"); @@ -381,7 +389,7 @@ describe('Alert Event Details', () => { it('sees osquery results from last action and add to a case', () => { loadRuleAlerts(ruleName); - cy.getBySel('expand-event').first().click({ force: true }); + cy.getBySel('expand-event').first().click(); cy.contains('Osquery Results').click(); cy.getBySel('osquery-results').should('exist'); cy.contains('select * from users;'); @@ -431,7 +439,7 @@ describe('Alert Event Details', () => { it('can visit discover from response action results', () => { const discoverRegex = new RegExp(`action_id: ${UUID_REGEX}`); loadRuleAlerts(ruleName); - cy.getBySel('expand-event').first().click({ force: true }); + cy.getBySel('expand-event').first().click(); cy.contains('Osquery Results').click(); cy.getBySel('osquery-results').should('exist'); checkActionItemsInResults({ @@ -472,7 +480,7 @@ describe('Alert Event Details', () => { it('can visit lens from response action results', () => { const lensRegex = new RegExp(`Action ${UUID_REGEX} results`); loadRuleAlerts(ruleName); - cy.getBySel('expand-event').first().click({ force: true }); + cy.getBySel('expand-event').first().click(); cy.contains('Osquery Results').click(); cy.getBySel('osquery-results').should('exist'); checkActionItemsInResults({ @@ -522,7 +530,7 @@ describe('Alert Event Details', () => { const timelineRegex = new RegExp(`Added ${UUID_REGEX} to timeline`); const filterRegex = new RegExp(`action_id: "${UUID_REGEX}"`); loadRuleAlerts(ruleName); - cy.getBySel('expand-event').first().click({ force: true }); + cy.getBySel('expand-event').first().click(); cy.contains('Osquery Results').click(); cy.getBySel('osquery-results').should('exist'); checkActionItemsInResults({ @@ -565,7 +573,7 @@ describe('Alert Event Details', () => { let initialNotificationCount: number; let updatedNotificationCount: number; loadRuleAlerts(ruleName); - cy.getBySel('expand-event').first().click({ force: true }); + cy.getBySel('expand-event').first().click(); cy.getBySel('osquery-actions-notification') .should('not.have.text', '0') .then((element) => { @@ -606,7 +614,7 @@ describe('Alert Event Details', () => { it('should be able to run take action query against all enrolled agents', () => { loadRuleAlerts(ruleName); - cy.getBySel('expand-event').first().click({ force: true }); + cy.getBySel('expand-event').first().click(); cy.getBySel('take-action-dropdown-btn').click(); cy.getBySel('osquery-action-item').click(); cy.getBySel('agentSelection').within(() => { @@ -620,8 +628,8 @@ describe('Alert Event Details', () => { cy.wait(1000); submitQuery(); cy.getBySel('flyout-body-osquery').within(() => { - // at least 2 agents should have responded - cy.get('[data-grid-row-index]').should('have.length.at.least', 2); + // at least 2 agents should have responded, sometimes it takes a while for the agents to respond + cy.get('[data-grid-row-index]', { timeout: 6000000 }).should('have.length.at.least', 2); }); }); }); @@ -643,7 +651,7 @@ describe('Alert Event Details', () => { it('should substitute params in osquery ran from timelines alerts', () => { loadRuleAlerts(ruleName); - cy.getBySel('send-alert-to-timeline-button').first().click({ force: true }); + cy.getBySel('send-alert-to-timeline-button').first().click(); cy.getBySel('query-events-table').within(() => { cy.getBySel('expand-event').first().click(); }); diff --git a/x-pack/plugins/osquery/cypress/tasks/live_query.ts b/x-pack/plugins/osquery/cypress/tasks/live_query.ts index b241366995eda..51213ee57f8c3 100644 --- a/x-pack/plugins/osquery/cypress/tasks/live_query.ts +++ b/x-pack/plugins/osquery/cypress/tasks/live_query.ts @@ -159,7 +159,5 @@ export const takeOsqueryActionWithParams = () => { cy.getBySel('osqueryColumnValueSelect').type('platform_like{downArrow}{enter}'); cy.wait(1000); submitQuery(); - cy.getBySel('dataGridHeader').within(() => { - cy.contains('tags'); - }); + cy.getBySel('dataGridHeader').should('contain', 'tags', { timeout: 6000000 }); }; diff --git a/x-pack/plugins/profiling/server/lib/setup/steps/get_fleet_policy_step.ts b/x-pack/plugins/profiling/server/lib/setup/steps/get_fleet_policy_step.ts index 7f7222a8a6e9d..a89be86280114 100644 --- a/x-pack/plugins/profiling/server/lib/setup/steps/get_fleet_policy_step.ts +++ b/x-pack/plugins/profiling/server/lib/setup/steps/get_fleet_policy_step.ts @@ -101,6 +101,35 @@ export function getFleetPolicyStep({ ...omit(apmPolicy, 'id', 'revision', 'updated_at', 'updated_by'), inputs: modifiedPolicyInputs, }); + + // We add here the creation of the new package_policy for symbolizer. + // We create the new policy and bind it to the Cloud default agent policy; + // to do so, force s required to be set to true. + const cloudAgentPolicyId = 'policy-elastic-agent-on-cloud'; + await packagePolicyClient.create( + soClient, + esClient, + { + policy_id: cloudAgentPolicyId, + enabled: true, + package: { + name: 'profiler_symbolizer', + title: 'Universal Profiling Symbolizer', + version: '8.8.0-preview', + }, + name: 'elastic-universal-profiling-symbolizer', + namespace: 'default', + inputs: [ + { + policy_template: 'universal_profiling_symbolizer', + enabled: true, + streams: [], + type: 'pf-elastic-symbolizer', + }, + ], + }, + { force: true } + ); }, }; } diff --git a/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.test.ts b/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.test.ts index 597396a5837ff..9dd2deb876c61 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.test.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.test.ts @@ -389,6 +389,49 @@ describe('Put payload schema', () => { } `); }); + + test('passes through remote_indices when specified', () => { + expect( + getPutPayloadSchema(() => basePrivilegeNamesMap).validate({ + elasticsearch: { + remote_indices: [ + { + clusters: ['remote_cluster'], + names: ['remote_index'], + privileges: ['all'], + }, + ], + }, + }) + ).toMatchInlineSnapshot(` + Object { + "elasticsearch": Object { + "remote_indices": Array [ + Object { + "clusters": Array [ + "remote_cluster", + ], + "names": Array [ + "remote_index", + ], + "privileges": Array [ + "all", + ], + }, + ], + }, + } + `); + }); + + // This is important for backwards compatibility + test('does not set default value for remote_indices when not specified', () => { + expect(getPutPayloadSchema(() => basePrivilegeNamesMap).validate({})).toMatchInlineSnapshot(` + Object { + "elasticsearch": Object {}, + } + `); + }); }); describe('validateKibanaPrivileges', () => { diff --git a/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.ts b/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.ts index a52399d4a04ec..5c8a07d15000d 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.ts @@ -37,7 +37,7 @@ export const transformPutPayloadToElasticsearchRole = ( metadata: rolePayload.metadata, cluster: elasticsearch.cluster || [], indices: elasticsearch.indices || [], - remote_indices: elasticsearch.remote_indices || [], + remote_indices: elasticsearch.remote_indices, run_as: elasticsearch.run_as || [], applications: [ ...transformPrivilegesToElasticsearchPrivileges(application, kibana), diff --git a/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts b/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts index 1e59f7a5d6a0f..6adbe8975b0a9 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts @@ -325,7 +325,7 @@ describe('PUT role', () => { body: { cluster: [], indices: [], - remote_indices: [], + remote_indices: undefined, run_as: [], applications: [], }, @@ -359,7 +359,7 @@ describe('PUT role', () => { body: { cluster: [], indices: [], - remote_indices: [], + remote_indices: undefined, run_as: [], applications: [ { @@ -402,7 +402,7 @@ describe('PUT role', () => { body: { cluster: [], indices: [], - remote_indices: [], + remote_indices: undefined, run_as: [], applications: [ { @@ -443,7 +443,7 @@ describe('PUT role', () => { body: { cluster: [], indices: [], - remote_indices: [], + remote_indices: undefined, run_as: [], applications: [ { @@ -480,6 +480,18 @@ describe('PUT role', () => { query: `{ "match": { "title": "foo" } }`, }, ], + remote_indices: [ + { + field_security: { + grant: ['test-field-security-grant-1', 'test-field-security-grant-2'], + except: ['test-field-security-except-1', 'test-field-security-except-2'], + }, + clusters: ['test-cluster-name-1', 'test-cluster-name-2'], + names: ['test-index-name-1', 'test-index-name-2'], + privileges: ['test-index-privilege-1', 'test-index-privilege-2'], + query: `{ "match": { "title": "foo" } }`, + }, + ], run_as: ['test-run-as-1', 'test-run-as-2'], }, kibana: [ @@ -539,7 +551,18 @@ describe('PUT role', () => { query: `{ "match": { "title": "foo" } }`, }, ], - remote_indices: [], + remote_indices: [ + { + field_security: { + grant: ['test-field-security-grant-1', 'test-field-security-grant-2'], + except: ['test-field-security-except-1', 'test-field-security-except-2'], + }, + clusters: ['test-cluster-name-1', 'test-cluster-name-2'], + names: ['test-index-name-1', 'test-index-name-2'], + privileges: ['test-index-privilege-1', 'test-index-privilege-2'], + query: `{ "match": { "title": "foo" } }`, + }, + ], metadata: { foo: 'test-metadata' }, run_as: ['test-run-as-1', 'test-run-as-2'], }, @@ -661,7 +684,7 @@ describe('PUT role', () => { query: `{ "match": { "title": "foo" } }`, }, ], - remote_indices: [], + remote_indices: undefined, metadata: { foo: 'test-metadata' }, run_as: ['test-run-as-1', 'test-run-as-2'], }, @@ -765,7 +788,7 @@ describe('PUT role', () => { privileges: ['test-index-privilege-1', 'test-index-privilege-2'], }, ], - remote_indices: [], + remote_indices: undefined, metadata: { foo: 'test-metadata' }, run_as: ['test-run-as-1', 'test-run-as-2'], }, @@ -803,7 +826,7 @@ describe('PUT role', () => { body: { cluster: [], indices: [], - remote_indices: [], + remote_indices: undefined, run_as: [], applications: [ { @@ -848,7 +871,7 @@ describe('PUT role', () => { body: { cluster: [], indices: [], - remote_indices: [], + remote_indices: undefined, run_as: [], applications: [ { @@ -893,7 +916,7 @@ describe('PUT role', () => { body: { cluster: [], indices: [], - remote_indices: [], + remote_indices: undefined, run_as: [], applications: [ { diff --git a/x-pack/plugins/security_solution/common/index.ts b/x-pack/plugins/security_solution/common/index.ts index 85546afccb694..3133996d79ed8 100644 --- a/x-pack/plugins/security_solution/common/index.ts +++ b/x-pack/plugins/security_solution/common/index.ts @@ -8,6 +8,7 @@ // TODO(jbudz): should be removed when upgrading to TS@4.8 // this is a skip for the errors created when typechecking with isolatedModules export {}; +export { APP_UI_ID, SecurityPageName } from './constants'; export { ELASTIC_SECURITY_RULE_ID } from './detection_engine/constants'; // Careful of exporting anything from this file as any file(s) you export here will cause your page bundle size to increase. diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/detection_page_filters.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/detection_page_filters.cy.ts index c16ccf35edbcc..a1a064eee6dc1 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/detection_page_filters.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/detection_page_filters.cy.ts @@ -39,6 +39,7 @@ import { addNewFilterGroupControlValues, deleteFilterGroupControl, discardFilterGroupControls, + editFilterGroupControl, editFilterGroupControls, saveFilterGroupControls, } from '../../tasks/common/filter_group'; @@ -97,7 +98,7 @@ const assertFilterControlsWithFilterObject = (filterObject = DEFAULT_DETECTION_P }); }; -describe.skip('Detections : Page Filters', { testIsolation: false }, () => { +describe('Detections : Page Filters', { testIsolation: false }, () => { before(() => { cleanKibana(); login(); @@ -116,10 +117,23 @@ describe.skip('Detections : Page Filters', { testIsolation: false }, () => { }); context('Alert Page Filters Customization ', { testIsolation: false }, () => { - it('Add New Controls', () => { + beforeEach(() => { + resetFilters(); + }); + it('should be able to delete Controls', () => { + waitForPageFilters(); + editFilterGroupControls(); + deleteFilterGroupControl(3); + cy.get(CONTROL_FRAMES).should((sub) => { + expect(sub.length).lt(4); + }); + discardFilterGroupControls(); + }); + it('should be able to add new Controls', () => { const fieldName = 'event.module'; const label = 'EventModule'; editFilterGroupControls(); + deleteFilterGroupControl(3); addNewFilterGroupControlValues({ fieldName, label, @@ -129,18 +143,20 @@ describe.skip('Detections : Page Filters', { testIsolation: false }, () => { discardFilterGroupControls(); cy.get(CONTROL_FRAME_TITLE).should('not.contain.text', label); }); - it('Delete Controls', () => { - waitForPageFilters(); + it('should be able to edit Controls', () => { + const fieldName = 'event.module'; + const label = 'EventModule'; editFilterGroupControls(); - deleteFilterGroupControl(3); - cy.get(CONTROL_FRAMES).should((sub) => { - expect(sub.length).lt(4); - }); + editFilterGroupControl({ idx: 3, fieldName, label }); + cy.get(CONTROL_FRAME_TITLE).should('contain.text', label); + cy.get(FILTER_GROUP_SAVE_CHANGES_POPOVER).should('be.visible'); discardFilterGroupControls(); + cy.get(CONTROL_FRAME_TITLE).should('not.contain.text', label); }); it('should not sync to the URL in edit mode but only in view mode', () => { cy.url().then((urlString) => { editFilterGroupControls(); + deleteFilterGroupControl(3); addNewFilterGroupControlValues({ fieldName: 'event.module', label: 'Event Module' }); cy.url().should('eq', urlString); saveFilterGroupControls(); diff --git a/x-pack/plugins/security_solution/cypress/screens/common/filter_group.ts b/x-pack/plugins/security_solution/cypress/screens/common/filter_group.ts index f78fbcfa9e476..b36229a5cbeaa 100644 --- a/x-pack/plugins/security_solution/cypress/screens/common/filter_group.ts +++ b/x-pack/plugins/security_solution/cypress/screens/common/filter_group.ts @@ -45,12 +45,12 @@ export const DETECTION_PAGE_FILTER_GROUP_CONTEXT_MENU = '[data-test-subj="filter export const DETECTION_PAGE_FILTER_GROUP_RESET_BUTTON = '[data-test-subj="filter-group__context--reset"]'; -export const FILTER_GROUP_CONTEXT_EDIT_CONTROLS = '[data-test-subj="filter_group__context--edit"]'; +export const FILTER_GROUP_CONTEXT_EDIT_CONTROLS = '[data-test-subj="filter-group__context--edit"]'; -export const FILTER_GROUP_CONTEXT_SAVE_CONTROLS = '[data-test-subj="filter_group__context--save"]'; +export const FILTER_GROUP_CONTEXT_SAVE_CONTROLS = '[data-test-subj="filter-group__context--save"]'; export const FILTER_GROUP_CONTEXT_DISCARD_CHANGES = - '[data-test-subj="filter_group__context--discard"]'; + '[data-test-subj="filter-group__context--discard"]'; export const FILTER_GROUP_ADD_CONTROL = '[data-test-subj="filter-group__add-control"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/common/filter_group.ts b/x-pack/plugins/security_solution/cypress/tasks/common/filter_group.ts index e006e2cc030a4..e12d705ab1ca7 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/common/filter_group.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/common/filter_group.ts @@ -22,6 +22,7 @@ import { DETECTION_PAGE_FILTERS_LOADING, OPTION_LISTS_LOADING, FILTER_GROUP_CONTEXT_DISCARD_CHANGES, + FILTER_GROUP_CONTROL_ACTION_EDIT, } from '../../screens/common/filter_group'; import { waitForPageFilters } from '../alerts'; @@ -90,3 +91,25 @@ export const deleteFilterGroupControl = (idx: number) => { cy.get(FILTER_GROUP_CONTROL_CONFIRM_DIALOG).should('be.visible'); cy.get(FILTER_GROUP_CONTROL_CONFIRM_BTN).trigger('click'); }; + +export const editFilterGroupControl = ({ + idx, + fieldName, + label, +}: { + idx: number; + fieldName: string; + label: string; +}) => { + cy.get(CONTROL_FRAME_TITLE).eq(idx).trigger('mouseover'); + cy.get(FILTER_GROUP_CONTROL_ACTION_EDIT(idx)).trigger('click', { force: true }); + const { FIELD_SEARCH, FIELD_PICKER, FIELD_LABEL, SAVE } = FILTER_GROUP_EDIT_CONTROL_PANEL_ITEMS; + cy.get(FIELD_SEARCH).type(fieldName); + cy.get(FIELD_PICKER(fieldName)).should('exist').trigger('click'); + + cy.get(FIELD_LABEL).should('have.value', fieldName); + cy.get(FIELD_LABEL).clear(); + cy.get(FIELD_LABEL).type(label).should('have.value', label); + cy.get(SAVE).trigger('click'); + cy.get(FILTER_GROUP_EDIT_CONTROLS_PANEL).should('not.exist'); +}; diff --git a/x-pack/plugins/security_solution/public/app/deep_links/index.ts b/x-pack/plugins/security_solution/public/app/deep_links/index.ts index 7ea8a9f51f406..e0bd41655545c 100644 --- a/x-pack/plugins/security_solution/public/app/deep_links/index.ts +++ b/x-pack/plugins/security_solution/public/app/deep_links/index.ts @@ -78,7 +78,7 @@ import { USERS_PATH, } from '../../../common/constants'; import type { ExperimentalFeatures } from '../../../common/experimental_features'; -import { hasCapabilities, subscribeAppLinks } from '../../common/links'; +import { appLinks$, hasCapabilities } from '../../common/links'; import type { AppLinkItems } from '../../common/links/types'; export const FEATURE = { @@ -630,7 +630,7 @@ const formatDeepLinks = (appLinks: AppLinkItems): AppDeepLink[] => * Registers any change in appLinks to be updated in app deepLinks */ export const registerDeepLinksUpdater = (appUpdater$: Subject): Subscription => { - return subscribeAppLinks((appLinks) => { + return appLinks$.subscribe((appLinks) => { appUpdater$.next(() => ({ navLinkStatus: AppNavLinkStatus.hidden, // needed to prevent main security link to switch to visible after update deepLinks: formatDeepLinks(appLinks), diff --git a/x-pack/plugins/security_solution/public/common/components/cell_actions/mocks.ts b/x-pack/plugins/security_solution/public/common/components/cell_actions/mocks.ts new file mode 100644 index 0000000000000..bf9bd77e6b261 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/cell_actions/mocks.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const makeAction = (actionsName: string, icon: string = 'icon', order?: number) => ({ + id: actionsName, + type: actionsName, + order, + getIconType: () => icon, + getDisplayName: () => actionsName, + getDisplayNameTooltip: () => actionsName, + isCompatible: () => Promise.resolve(true), + execute: () => { + alert(actionsName); + return Promise.resolve(); + }, +}); diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/context_menu.tsx b/x-pack/plugins/security_solution/public/common/components/filter_group/context_menu.tsx index bf672a5fabfa4..b7dc2f6f32457 100644 --- a/x-pack/plugins/security_solution/public/common/components/filter_group/context_menu.tsx +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/context_menu.tsx @@ -48,7 +48,7 @@ export const FilterGroupContextMenu = () => { [toggleContextMenu] ); - const resetSelection = useCallback(() => { + const resetSelection = useCallback(async () => { if (!controlGroupInputUpdates) return; // remove existing embeddables @@ -56,9 +56,10 @@ export const FilterGroupContextMenu = () => { panels: {}, }); - initialControls.forEach((control, idx) => { - controlGroup?.addOptionsListControl({ - controlId: String(idx), + for (let counter = 0; counter < initialControls.length; counter++) { + const control = initialControls[counter]; + await controlGroup?.addOptionsListControl({ + controlId: String(counter), hideExclude: true, hideSort: true, hidePanelTitles: true, @@ -68,9 +69,8 @@ export const FilterGroupContextMenu = () => { dataViewId: dataViewId ?? '', ...control, }); - }); + } - controlGroup?.reload(); switchToViewMode(); setShowFiltersChangedBanner(false); }, [ diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/filter_group.test.tsx b/x-pack/plugins/security_solution/public/common/components/filter_group/filter_group.test.tsx index f066b022af04e..5da07f8add864 100644 --- a/x-pack/plugins/security_solution/public/common/components/filter_group/filter_group.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/filter_group.test.tsx @@ -462,14 +462,11 @@ describe(' Filter Group Component ', () => { controlGroupMock.addOptionsListControl.mockClear(); controlGroupMock.updateInput.mockClear(); - controlGroupMock.reload.mockClear(); fireEvent.click(screen.getByTestId(TEST_IDS.CONTEXT_MENU.RESET)); await waitFor(() => { // blanks the input expect(controlGroupMock.updateInput.mock.calls.length).toBe(2); - expect(controlGroupMock.reload.mock.calls.length).toBe(1); - expect(controlGroupMock.addOptionsListControl.mock.calls.length).toBe(4); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/index.tsx b/x-pack/plugins/security_solution/public/common/components/filter_group/index.tsx index 533bf19146dad..a4c61fb9c98a6 100644 --- a/x-pack/plugins/security_solution/public/common/components/filter_group/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/index.tsx @@ -334,7 +334,7 @@ const FilterGroupComponent = (props: PropsWithChildren) => { setShowFiltersChangedBanner(false); }, [controlGroup, switchToViewMode, getStoredControlInput, hasPendingChanges]); - const upsertPersistableControls = useCallback(() => { + const upsertPersistableControls = useCallback(async () => { const persistableControls = initialControls.filter((control) => control.persist === true); if (persistableControls.length > 0) { const currentPanels = Object.values(controlGroup?.getInput().panels ?? []) as Array< @@ -342,7 +342,7 @@ const FilterGroupComponent = (props: PropsWithChildren) => { >; const orderedPanels = currentPanels.sort((a, b) => a.order - b.order); let filterControlsDeleted = false; - persistableControls.forEach((control) => { + for (const control of persistableControls) { const controlExists = currentPanels.some( (currControl) => control.fieldName === currControl.explicitInput.fieldName ); @@ -354,7 +354,7 @@ const FilterGroupComponent = (props: PropsWithChildren) => { } // add persitable controls - controlGroup?.addOptionsListControl({ + await controlGroup?.addOptionsListControl({ title: control.title, hideExclude: true, hideSort: true, @@ -367,21 +367,22 @@ const FilterGroupComponent = (props: PropsWithChildren) => { ...control, }); } - }); - orderedPanels.forEach((panel) => { + } + + for (const panel of orderedPanels) { if (panel.explicitInput.fieldName) - controlGroup?.addOptionsListControl({ + await controlGroup?.addOptionsListControl({ selectedOptions: [], fieldName: panel.explicitInput.fieldName, dataViewId: dataViewId ?? '', ...panel.explicitInput, }); - }); + } } }, [controlGroup, dataViewId, initialControls]); - const saveChangesHandler = useCallback(() => { - upsertPersistableControls(); + const saveChangesHandler = useCallback(async () => { + await upsertPersistableControls(); switchToViewMode(); setShowFiltersChangedBanner(false); }, [switchToViewMode, upsertPersistableControls]); diff --git a/x-pack/plugins/security_solution/public/common/components/filter_group/mocks/index.tsx b/x-pack/plugins/security_solution/public/common/components/filter_group/mocks/index.tsx index 3fa2ff03318cb..d31285ccb3761 100644 --- a/x-pack/plugins/security_solution/public/common/components/filter_group/mocks/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/filter_group/mocks/index.tsx @@ -8,7 +8,7 @@ import type { ControlGroupContainer } from '@kbn/controls-plugin/public'; import type { Filter } from '@kbn/es-query'; import type { FC } from 'react'; -import React from 'react'; +import React, { useEffect } from 'react'; import { TEST_IDS } from '../constants'; import type { FilterGroupProps } from '../types'; import { getControlGroupMock } from './control_group'; @@ -61,15 +61,17 @@ import { getControlGroupMock } from './control_group'; *}); * */ -export function getMockedFilterGroupWithCustomFilters(outputFilters: Filter[] | undefined) { +export function getMockedFilterGroupWithCustomFilters(outputFilters?: Filter[]) { const FilterGroup: FC = ({ onInit, onFilterChange }) => { - if (onInit) { - onInit(getControlGroupMock() as unknown as ControlGroupContainer); - } + useEffect(() => { + if (onInit) { + onInit(getControlGroupMock() as unknown as ControlGroupContainer); + } - if (onFilterChange) { - onFilterChange(outputFilters ?? []); - } + if (onFilterChange) { + onFilterChange(outputFilters ?? []); + } + }, [onInit, onFilterChange]); return
; }; diff --git a/x-pack/plugins/security_solution/public/common/components/ml/score/score_health.tsx b/x-pack/plugins/security_solution/public/common/components/ml/score/score_health.tsx index 4fb1168e1af3a..ac179b2ac8337 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/score/score_health.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/score/score_health.tsx @@ -22,7 +22,7 @@ export const ScoreHealth = React.memo(({ score }) => { ScoreHealth.displayName = 'ScoreHealth'; -// ಠ_ಠ A hard-fork of the `ml` ml/common/util/anomaly_utils.js#getSeverityColor ಠ_ಠ +// ಠ_ಠ A hard-fork of the @kbn/ml-anomaly-utils;#getSeverityColor ಠ_ಠ // // Returns a severity label (one of critical, major, minor, warning, low or unknown) // for the supplied normalized anomaly score (a value between 0 and 100), where scores diff --git a/x-pack/plugins/security_solution/public/common/components/ml/types.ts b/x-pack/plugins/security_solution/public/common/components/ml/types.ts index 2a969dc3d15cb..24c0d9314735d 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/ml/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { Influencer } from '@kbn/ml-plugin/public'; +import type { MlInfluencer } from '@kbn/ml-anomaly-utils'; import type { FlowTarget } from '../../../../common/search_strategy'; import type { HostsType } from '../../../explore/hosts/store/model'; @@ -31,7 +31,7 @@ export interface Source { function_description: string; typical: number[]; actual: number[]; - influencers: Influencer[]; + influencers: MlInfluencer[]; } export interface CriteriaFields { diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/nav_links.test.ts b/x-pack/plugins/security_solution/public/common/components/navigation/nav_links.test.ts deleted file mode 100644 index c44873414ca11..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/navigation/nav_links.test.ts +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { renderHook } from '@testing-library/react-hooks'; -import { SecurityPageName } from '../../../app/types'; -import type { AppLinkItems } from '../../links'; -import { TestProviders } from '../../mock'; -import { useAppNavLinks, useAppRootNavLink } from './nav_links'; -import type { NavLinkItem } from './types'; - -const mockNavLinks: AppLinkItems = [ - { - description: 'description', - id: SecurityPageName.administration, - links: [ - { - description: 'description 2', - id: SecurityPageName.endpoints, - links: [], - path: '/path_2', - title: 'title 2', - sideNavDisabled: true, - landingIcon: 'someicon', - landingImage: 'someimage', - skipUrlState: true, - }, - ], - path: '/path', - title: 'title', - }, -]; - -jest.mock('../../links', () => ({ - useAppLinks: () => mockNavLinks, -})); - -const renderUseAppNavLinks = () => - renderHook<{}, NavLinkItem[]>(() => useAppNavLinks(), { wrapper: TestProviders }); - -const renderUseAppRootNavLink = (id: SecurityPageName) => - renderHook<{ id: SecurityPageName }, NavLinkItem | undefined>(() => useAppRootNavLink(id), { - wrapper: TestProviders, - }); - -describe('useAppNavLinks', () => { - it('should return all nav links', () => { - const { result } = renderUseAppNavLinks(); - expect(result.current).toMatchInlineSnapshot(` - Array [ - Object { - "description": "description", - "id": "administration", - "links": Array [ - Object { - "description": "description 2", - "disabled": true, - "icon": "someicon", - "id": "endpoints", - "image": "someimage", - "skipUrlState": true, - "title": "title 2", - }, - ], - "title": "title", - }, - ] - `); - }); - - it('should return a root nav links', () => { - const { result } = renderUseAppRootNavLink(SecurityPageName.administration); - expect(result.current).toMatchInlineSnapshot(` - Object { - "description": "description", - "id": "administration", - "links": Array [ - Object { - "description": "description 2", - "disabled": true, - "icon": "someicon", - "id": "endpoints", - "image": "someimage", - "skipUrlState": true, - "title": "title 2", - }, - ], - "title": "title", - } - `); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.test.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.test.tsx index acf7765ffa936..a0612e9b63897 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.test.tsx @@ -51,9 +51,9 @@ jest.mock('../../../links', () => ({ getAncestorLinksInfo: (id: string) => [{ id }], })); -const mockUseAppNavLinks = jest.fn(); -jest.mock('../nav_links', () => ({ - useAppNavLinks: () => mockUseAppNavLinks(), +const mockUseNavLinks = jest.fn(); +jest.mock('../../../links/nav_links', () => ({ + useNavLinks: () => mockUseNavLinks(), })); jest.mock('../../links', () => ({ useGetSecuritySolutionLinkProps: @@ -80,14 +80,14 @@ const renderNav = () => describe('SecuritySideNav', () => { beforeEach(() => { jest.clearAllMocks(); - mockUseAppNavLinks.mockReturnValue([alertsNavLink, manageNavLink]); + mockUseNavLinks.mockReturnValue([alertsNavLink, manageNavLink]); useKibana().services.chrome.hasHeaderBanner$ = jest.fn(() => new BehaviorSubject(false).asObservable() ); }); it('should render main items', () => { - mockUseAppNavLinks.mockReturnValue([alertsNavLink]); + mockUseNavLinks.mockReturnValue([alertsNavLink]); renderNav(); expect(mockSolutionSideNav).toHaveBeenCalledWith({ selectedId: SecurityPageName.alerts, @@ -104,7 +104,7 @@ describe('SecuritySideNav', () => { }); it('should render the loader if items are still empty', () => { - mockUseAppNavLinks.mockReturnValue([]); + mockUseNavLinks.mockReturnValue([]); const result = renderNav(); expect(result.getByTestId('sideNavLoader')).toBeInTheDocument(); expect(mockSolutionSideNav).not.toHaveBeenCalled(); @@ -121,7 +121,7 @@ describe('SecuritySideNav', () => { }); it('should render footer items', () => { - mockUseAppNavLinks.mockReturnValue([manageNavLink]); + mockUseNavLinks.mockReturnValue([manageNavLink]); renderNav(); expect(mockSolutionSideNav).toHaveBeenCalledWith( expect.objectContaining({ @@ -148,7 +148,7 @@ describe('SecuritySideNav', () => { }); it('should not render disabled items', () => { - mockUseAppNavLinks.mockReturnValue([{ ...alertsNavLink, disabled: true }, manageNavLink]); + mockUseNavLinks.mockReturnValue([{ ...alertsNavLink, disabled: true }, manageNavLink]); renderNav(); expect(mockSolutionSideNav).toHaveBeenCalledWith( expect.objectContaining({ @@ -163,7 +163,7 @@ describe('SecuritySideNav', () => { }); it('should render custom item', () => { - mockUseAppNavLinks.mockReturnValue([{ id: SecurityPageName.landing, title: 'get started' }]); + mockUseNavLinks.mockReturnValue([{ id: SecurityPageName.landing, title: 'get started' }]); renderNav(); expect(mockSolutionSideNav).toHaveBeenCalledWith( expect.objectContaining({ diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.tsx index f647ff1f873e1..b34e069c24860 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.tsx @@ -13,7 +13,7 @@ import { SecurityPageName } from '../../../../app/types'; import { getAncestorLinksInfo } from '../../../links'; import { useRouteSpy } from '../../../utils/route/use_route_spy'; import { useGetSecuritySolutionLinkProps } from '../../links'; -import { useAppNavLinks } from '../nav_links'; +import { useNavLinks } from '../../../links/nav_links'; import { useShowTimeline } from '../../../utils/timeline/use_show_timeline'; import { useIsPolicySettingsBarVisible } from '../../../../management/pages/policy/view/policy_hooks'; import { track } from '../../../lib/telemetry'; @@ -30,7 +30,7 @@ const isGetStartedNavItem = (id: SecurityPageName) => id === SecurityPageName.la * Returns the formatted `items` and `footerItems` to be rendered in the navigation */ const useSolutionSideNavItems = () => { - const navLinks = useAppNavLinks(); + const navLinks = useNavLinks(); const getSecuritySolutionLinkProps = useGetSecuritySolutionLinkProps(); // adds href and onClick props const sideNavItems = useMemo(() => { diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_primary_navigation.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_primary_navigation.tsx index 647193357b66b..867cda2bcf4e8 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_primary_navigation.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/use_security_solution_navigation/use_primary_navigation.tsx @@ -9,10 +9,12 @@ import React, { useEffect, useState, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import type { KibanaPageTemplateProps } from '@kbn/shared-ux-page-kibana-template'; +import useObservable from 'react-use/lib/useObservable'; import type { PrimaryNavigationProps } from './types'; import { usePrimaryNavigationItems } from './use_navigation_items'; import { useIsGroupedNavigationEnabled } from '../helpers'; import { SecuritySideNav } from '../security_side_nav'; +import { useKibana } from '../../../lib/kibana'; const translatedNavTitle = i18n.translate('xpack.securitySolution.navigation.mainLabel', { defaultMessage: 'Security', @@ -45,6 +47,14 @@ export const usePrimaryNavigation = ({ selectedTabId, }); + const { isSidebarEnabled$ } = useKibana().services; + + const isSidebarEnabled = useObservable(isSidebarEnabled$); + + if (!isSidebarEnabled) { + return undefined; + } + return { canBeCollapsed: true, name: translatedNavTitle, diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/types.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/types.ts index 399725db13383..46c18e19f12fd 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/types.ts @@ -5,9 +5,14 @@ * 2.0. */ -import type { TypedLensByValueInput } from '@kbn/lens-plugin/public'; +import type { + DatatableVisualizationState, + FormBasedPersistedState, + TypedLensByValueInput, +} from '@kbn/lens-plugin/public'; +import type { DataViewSpec } from '@kbn/data-views-plugin/common'; import type { Action } from '@kbn/ui-actions-plugin/public'; -import type { Filter } from '@kbn/es-query'; +import type { Filter, Query } from '@kbn/es-query'; import type { InputsModelId } from '../../store/inputs/constants'; import type { SourcererScopeName } from '../../store/sourcerer/model'; @@ -143,3 +148,36 @@ export interface VisualizationResponse hits: Hit[]; }; } + +export interface SavedObjectReference { + id: string; + name: string; + type: string; +} + +export interface LensDataTableAttributes { + description?: string; + references: SavedObjectReference[]; + visualizationType: TVisType; + state: { + query: Query; + globalPalette?: { + activePaletteId: string; + state?: unknown; + }; + filters: Filter[]; + adHocDataViews?: Record; + internalReferences?: SavedObjectReference[]; + datasourceStates: { + formBased: FormBasedPersistedState; + }; + visualization: TVisState; + }; + title: string; +} + +export interface LensDataTableEmbeddable { + attributes: LensDataTableAttributes<'lnsDatatable', DatatableVisualizationState>; + id: string; + timeRange: { from: string; to: string; fromStr: string; toStr: string }; +} diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts index 42a34fa08fce3..702e3f2bc39e8 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts @@ -45,6 +45,8 @@ import { triggersActionsUiMock } from '@kbn/triggers-actions-ui-plugin/public/mo import { mockApm } from '../apm/service.mock'; import { cloudExperimentsMock } from '@kbn/cloud-experiments-plugin/common/mocks'; import { guidedOnboardingMock } from '@kbn/guided-onboarding-plugin/public/mocks'; +import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; +import { of } from 'rxjs'; const mockUiSettings: Record = { [DEFAULT_TIME_RANGE]: { from: 'now-15m', to: 'now', mode: 'quick' }, @@ -104,6 +106,7 @@ export const createStartServicesMock = ( const fleet = fleetMock.createStartMock(); const unifiedSearch = unifiedSearchPluginMock.createStartContract(); const cases = mockCasesContract(); + const dataViewServiceMock = dataViewPluginMocks.createStartContract(); cases.helpers.getUICapabilities.mockReturnValue(noCasesPermissions()); const triggersActionsUi = triggersActionsUiMock.createStart(); const cloudExperiments = cloudExperimentsMock.createStartMock(); @@ -114,6 +117,7 @@ export const createStartServicesMock = ( apm, cases, unifiedSearch, + dataViews: dataViewServiceMock, data: { ...data, dataViews: { @@ -183,6 +187,7 @@ export const createStartServicesMock = ( triggersActionsUi, cloudExperiments, guidedOnboarding, + isSidebarEnabled$: of(true), } as unknown as StartServices; }; diff --git a/x-pack/plugins/security_solution/public/common/links/links.ts b/x-pack/plugins/security_solution/public/common/links/links.ts index de30840d02d9d..be1f206e339b2 100644 --- a/x-pack/plugins/security_solution/public/common/links/links.ts +++ b/x-pack/plugins/security_solution/public/common/links/links.ts @@ -7,7 +7,8 @@ import type { Capabilities } from '@kbn/core/public'; import { get, isArray } from 'lodash'; -import { useEffect, useState } from 'react'; +import { useMemo } from 'react'; +import useObservable from 'react-use/lib/useObservable'; import { BehaviorSubject } from 'rxjs'; import type { SecurityPageName } from '../../../common/constants'; import type { @@ -20,72 +21,48 @@ import type { } from './types'; /** - * App links updater, it keeps the value of the app links in sync with all application. - * It can be updated using `updateAppLinks` or `excludeAppLink` - * Read it using `subscribeAppLinks` or `useAppLinks` hook. + * App links updater, it stores the `appLinkItems` recursive hierarchy and keeps + * the value of the app links in sync with all application components. + * It can be updated using `updateAppLinks`. + * Read it using subscription or `useAppLinks` hook. */ -const appLinksUpdater$ = new BehaviorSubject<{ - links: AppLinkItems; - normalizedLinks: NormalizedLinks; -}>({ - links: [], // stores the appLinkItems recursive hierarchy - normalizedLinks: {}, // stores a flatten normalized object for direct id access -}); +const appLinksUpdater$ = new BehaviorSubject([]); +// stores a flatten normalized appLinkItems object for internal direct id access +const normalizedAppLinksUpdater$ = new BehaviorSubject({}); -const getAppLinksValue = (): AppLinkItems => appLinksUpdater$.getValue().links; -const getNormalizedLinksValue = (): NormalizedLinks => appLinksUpdater$.getValue().normalizedLinks; +// AppLinks observable +export const appLinks$ = appLinksUpdater$.asObservable(); /** - * Subscribes to the updater to get the app links updates + * Updates the app links applying the filter by permissions */ -export const subscribeAppLinks = (onChange: (links: AppLinkItems) => void) => - appLinksUpdater$.subscribe(({ links }) => onChange(links)); +export const updateAppLinks = ( + appLinksToUpdate: AppLinkItems, + linksPermissions: LinksPermissions +) => { + const filteredAppLinks = getFilteredAppLinks(appLinksToUpdate, linksPermissions); + appLinksUpdater$.next(Object.freeze(filteredAppLinks)); + normalizedAppLinksUpdater$.next(Object.freeze(getNormalizedLinks(filteredAppLinks))); +}; /** * Hook to get the app links updated value */ -export const useAppLinks = (): AppLinkItems => { - const [appLinks, setAppLinks] = useState(getAppLinksValue); - - useEffect(() => { - const linksSubscription = subscribeAppLinks((newAppLinks) => { - setAppLinks(newAppLinks); - }); - return () => linksSubscription.unsubscribe(); - }, []); - - return appLinks; -}; +export const useAppLinks = (): AppLinkItems => + useObservable(appLinksUpdater$, appLinksUpdater$.getValue()); +/** + * Hook to get the normalized app links updated value + */ +export const useNormalizedAppLinks = (): NormalizedLinks => + useObservable(normalizedAppLinksUpdater$, normalizedAppLinksUpdater$.getValue()); /** * Hook to check if a link exists in the application links, * It can be used to know if a link access is authorized. */ export const useLinkExists = (id: SecurityPageName): boolean => { - const [linkExists, setLinkExists] = useState(!!getNormalizedLink(id)); - - useEffect(() => { - const linksSubscription = subscribeAppLinks(() => { - setLinkExists(!!getNormalizedLink(id)); - }); - return () => linksSubscription.unsubscribe(); - }, [id]); - - return linkExists; -}; - -/** - * Updates the app links applying the filter by permissions - */ -export const updateAppLinks = ( - appLinksToUpdate: AppLinkItems, - linksPermissions: LinksPermissions -) => { - const filteredAppLinks = getFilteredAppLinks(appLinksToUpdate, linksPermissions); - appLinksUpdater$.next({ - links: Object.freeze(filteredAppLinks), - normalizedLinks: Object.freeze(getNormalizedLinks(filteredAppLinks)), - }); + const normalizedLinks = useNormalizedAppLinks(); + return useMemo(() => !!normalizedLinks[id], [normalizedLinks, id]); }; /** @@ -128,6 +105,10 @@ export const needsUrlState = (id: SecurityPageName): boolean => { return !getNormalizedLink(id)?.skipUrlState; }; +export const getLinksWithHiddenTimeline = (): LinkInfo[] => { + return Object.values(normalizedAppLinksUpdater$.getValue()).filter((link) => link.hideTimeline); +}; + // Internal functions /** @@ -136,8 +117,8 @@ export const needsUrlState = (id: SecurityPageName): boolean => { const getNormalizedLinks = ( currentLinks: AppLinkItems, parentId?: SecurityPageName -): NormalizedLinks => { - return currentLinks.reduce((normalized, { links, ...currentLink }) => { +): NormalizedLinks => + currentLinks.reduce((normalized, { links, ...currentLink }) => { normalized[currentLink.id] = { ...currentLink, parentId, @@ -147,10 +128,9 @@ const getNormalizedLinks = ( } return normalized; }, {}); -}; const getNormalizedLink = (id: SecurityPageName): Readonly | undefined => - getNormalizedLinksValue()[id]; + normalizedAppLinksUpdater$.getValue()[id]; const getFilteredAppLinks = ( appLinkToFilter: AppLinkItems, @@ -226,7 +206,3 @@ const isLinkAllowed = ( } return true; }; - -export const getLinksWithHiddenTimeline = (): LinkInfo[] => { - return Object.values(getNormalizedLinksValue()).filter((link) => link.hideTimeline); -}; diff --git a/x-pack/plugins/security_solution/public/common/links/nav_links.test.ts b/x-pack/plugins/security_solution/public/common/links/nav_links.test.ts new file mode 100644 index 0000000000000..d8decac43a86a --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/links/nav_links.test.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { AppLinkItems } from './types'; +import { formatNavigationLinks } from './nav_links'; +import { SecurityPageName } from '../../app/types'; + +const mockNavLinks: AppLinkItems = [ + { + description: 'description', + id: SecurityPageName.administration, + links: [ + { + description: 'description 2', + id: SecurityPageName.endpoints, + links: [], + path: '/path_2', + title: 'title 2', + sideNavDisabled: true, + landingIcon: 'someicon', + landingImage: 'someimage', + skipUrlState: true, + }, + ], + path: '/path', + title: 'title', + }, +]; + +describe('formatNavigationLinks', () => { + it('should format links', () => { + expect(formatNavigationLinks(mockNavLinks)).toMatchInlineSnapshot(` + Array [ + Object { + "description": "description", + "id": "administration", + "links": Array [ + Object { + "description": "description 2", + "disabled": true, + "icon": "someicon", + "id": "endpoints", + "image": "someimage", + "skipUrlState": true, + "title": "title 2", + }, + ], + "title": "title", + }, + ] + `); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/nav_links.ts b/x-pack/plugins/security_solution/public/common/links/nav_links.ts similarity index 52% rename from x-pack/plugins/security_solution/public/common/components/navigation/nav_links.ts rename to x-pack/plugins/security_solution/public/common/links/nav_links.ts index 5fff0a9649940..0882bdf233601 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/nav_links.ts +++ b/x-pack/plugins/security_solution/public/common/links/nav_links.ts @@ -5,23 +5,13 @@ * 2.0. */ -import { useMemo } from 'react'; -import { useAppLinks } from '../../links'; -import type { SecurityPageName } from '../../../app/types'; -import type { NavLinkItem } from './types'; -import type { AppLinkItems } from '../../links/types'; +import useObservable from 'react-use/lib/useObservable'; +import { map } from 'rxjs'; +import { appLinks$ } from './links'; +import type { SecurityPageName } from '../../app/types'; +import type { AppLinkItems, NavigationLink } from './types'; -export const useAppNavLinks = (): NavLinkItem[] => { - const appLinks = useAppLinks(); - const navLinks = useMemo(() => formatNavLinkItems(appLinks), [appLinks]); - return navLinks; -}; - -export const useAppRootNavLink = (linkId: SecurityPageName): NavLinkItem | undefined => { - return useAppNavLinks().find(({ id }) => id === linkId); -}; - -const formatNavLinkItems = (appLinks: AppLinkItems): NavLinkItem[] => +export const formatNavigationLinks = (appLinks: AppLinkItems): NavigationLink[] => appLinks.map((link) => ({ id: link.id, title: link.title, @@ -33,9 +23,21 @@ const formatNavLinkItems = (appLinks: AppLinkItems): NavLinkItem[] => ...(link.skipUrlState != null ? { skipUrlState: link.skipUrlState } : {}), ...(link.isBeta != null ? { isBeta: link.isBeta } : {}), ...(link.betaOptions != null ? { betaOptions: link.betaOptions } : {}), - ...(link.links && link.links.length - ? { - links: formatNavLinkItems(link.links), - } - : {}), + ...(link.links?.length && { + links: formatNavigationLinks(link.links), + }), })); + +/** + * Navigation links observable based on Security AppLinks, + * It is used to generate the side navigation items + */ +export const navLinks$ = appLinks$.pipe(map(formatNavigationLinks)); + +export const useNavLinks = (): NavigationLink[] => { + return useObservable(navLinks$, []); +}; + +export const useRootNavLink = (linkId: SecurityPageName): NavigationLink | undefined => { + return useNavLinks().find(({ id }) => id === linkId); +}; diff --git a/x-pack/plugins/security_solution/public/common/links/types.ts b/x-pack/plugins/security_solution/public/common/links/types.ts index f9a2c5776262f..162415fb66a16 100644 --- a/x-pack/plugins/security_solution/public/common/links/types.ts +++ b/x-pack/plugins/security_solution/public/common/links/types.ts @@ -131,3 +131,19 @@ export type AppLinkItems = Readonly; export type LinkInfo = Omit; export type NormalizedLink = LinkInfo & { parentId?: SecurityPageName }; export type NormalizedLinks = Partial>; + +export interface NavigationLink { + categories?: LinkCategories; + description?: string; + disabled?: boolean; + icon?: IconType; + id: SecurityPageName; + links?: NavigationLink[]; + image?: string; + title: string; + skipUrlState?: boolean; + isBeta?: boolean; + betaOptions?: { + text: string; + }; +} diff --git a/x-pack/plugins/security_solution/public/dashboards/components/edit_dashboard_button.test.tsx b/x-pack/plugins/security_solution/public/dashboards/components/edit_dashboard_button.test.tsx index e4cd6fa206929..43afa552d50fd 100644 --- a/x-pack/plugins/security_solution/public/dashboards/components/edit_dashboard_button.test.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/components/edit_dashboard_button.test.tsx @@ -5,12 +5,16 @@ * 2.0. */ -import { render } from '@testing-library/react'; +import type { RenderResult } from '@testing-library/react'; +import { fireEvent, render } from '@testing-library/react'; import React from 'react'; +import type { Query } from '@kbn/es-query'; + import { useKibana } from '../../common/lib/kibana'; -import { createStartServicesMock } from '../../common/lib/kibana/kibana_react.mock'; import { TestProviders } from '../../common/mock/test_providers'; +import type { EditDashboardButtonComponentProps } from './edit_dashboard_button'; import { EditDashboardButton } from './edit_dashboard_button'; +import { ViewMode } from '@kbn/embeddable-plugin/public'; jest.mock('../../common/lib/kibana/kibana_react', () => { return { @@ -24,18 +28,54 @@ describe('EditDashboardButton', () => { to: '2023-03-24T23:59:59.999Z', }; - beforeAll(() => { + const props = { + filters: [], + query: { query: '', language: '' } as Query, + savedObjectId: 'mockSavedObjectId', + timeRange, + }; + const servicesMock = { + dashboard: { locator: { getRedirectUrl: jest.fn() } }, + application: { + navigateToApp: jest.fn(), + navigateToUrl: jest.fn(), + }, + }; + + const renderButton = (testProps: EditDashboardButtonComponentProps) => { + return render( + + + + ); + }; + + let renderResult: RenderResult; + beforeEach(() => { (useKibana as jest.Mock).mockReturnValue({ - services: createStartServicesMock(), + services: servicesMock, }); + renderResult = renderButton(props); + }); + + beforeEach(() => { + jest.clearAllMocks(); }); it('should render', () => { - const { queryByTestId } = render( - - - + expect(renderResult.queryByTestId('dashboardEditButton')).toBeInTheDocument(); + }); + + it('should render dashboard edit url', () => { + fireEvent.click(renderResult.getByTestId('dashboardEditButton')); + expect(servicesMock.dashboard?.locator?.getRedirectUrl).toHaveBeenCalledWith( + expect.objectContaining({ + query: props.query, + filters: props.filters, + timeRange: props.timeRange, + dashboardId: props.savedObjectId, + viewMode: ViewMode.EDIT, + }) ); - expect(queryByTestId('dashboardEditButton')).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/security_solution/public/dashboards/components/edit_dashboard_button.tsx b/x-pack/plugins/security_solution/public/dashboards/components/edit_dashboard_button.tsx index c1089e95787e0..bd360229c7e1f 100644 --- a/x-pack/plugins/security_solution/public/dashboards/components/edit_dashboard_button.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/components/edit_dashboard_button.tsx @@ -5,12 +5,12 @@ * 2.0. */ -import React from 'react'; +import React, { useCallback } from 'react'; import type { Query, Filter } from '@kbn/es-query'; import { EuiButton } from '@elastic/eui'; -import { useDashboardAppLink } from '../hooks/use_dashboard_app_link'; +import { ViewMode } from '@kbn/embeddable-plugin/public'; import { EDIT_DASHBOARD_BUTTON_TITLE } from '../pages/details/translations'; -import { useKibana } from '../../common/lib/kibana'; +import { useKibana, useNavigation } from '../../common/lib/kibana'; export interface EditDashboardButtonComponentProps { filters?: Filter[]; @@ -31,24 +31,33 @@ const EditDashboardButtonComponent: React.FC timeRange, }) => { const { - services: { uiSettings }, + services: { dashboard }, } = useKibana(); + const { navigateTo } = useNavigation(); - const { onClick } = useDashboardAppLink({ - query, - filters, - timeRange, - uiSettings, - savedObjectId, - }); - + const onClick = useCallback( + (e) => { + e.preventDefault(); + const url = dashboard?.locator?.getRedirectUrl({ + query, + filters, + timeRange, + dashboardId: savedObjectId, + viewMode: ViewMode.EDIT, + }); + if (url) { + navigateTo({ url }); + } + }, + [dashboard?.locator, query, filters, timeRange, savedObjectId, navigateTo] + ); return ( {EDIT_DASHBOARD_BUTTON_TITLE} diff --git a/x-pack/plugins/security_solution/public/dashboards/hooks/use_dashboard_app_link.test.tsx b/x-pack/plugins/security_solution/public/dashboards/hooks/use_dashboard_app_link.test.tsx deleted file mode 100644 index decd108b6d6af..0000000000000 --- a/x-pack/plugins/security_solution/public/dashboards/hooks/use_dashboard_app_link.test.tsx +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { renderHook } from '@testing-library/react-hooks'; -import { useNavigation } from '../../common/lib/kibana'; -import { TestProviders } from '../../common/mock'; -import type { UseDashboardAppLinkProps } from './use_dashboard_app_link'; -import { useDashboardAppLink } from './use_dashboard_app_link'; - -jest.mock('../../common/lib/kibana', () => ({ - useNavigation: jest.fn(), -})); - -describe('useDashboardAppLink', () => { - const mockNavigateTo = jest.fn(); - const filters = [ - { - meta: { - index: 'security-solution-default', - type: 'phrase', - key: 'event.action', - params: { - query: 'host', - }, - disabled: false, - negate: false, - alias: null, - }, - query: { - match_phrase: { - 'event.action': 'host', - }, - }, - $state: { - store: 'appState', - }, - }, - ]; - const props = { - query: { - language: 'kuery', - query: '', - }, - filters: [], - timeRange: { - from: '2023-03-24T00:00:00.000Z', - fromStr: 'now/d', - to: '2023-03-24T23:59:59.999Z', - toStr: 'now/d', - }, - uiSettings: { - get: jest.fn(), - }, - savedObjectId: 'e2937420-c8ba-11ed-a7eb-3d08ee4d53cb', - }; - - beforeEach(() => { - jest.clearAllMocks(); - (useNavigation as jest.Mock).mockReturnValue({ - getAppUrl: jest - .fn() - .mockReturnValue('/app/dashboards#/view/e2937420-c8ba-11ed-a7eb-3d08ee4d53cb'), - navigateTo: mockNavigateTo, - }); - }); - it('create links to Dashboard app - with filters', () => { - const testProps = { ...props, filters } as unknown as UseDashboardAppLinkProps; - - const { result } = renderHook(() => useDashboardAppLink(testProps), { - wrapper: TestProviders, - }); - expect(result.current.href).toMatchInlineSnapshot( - `"/app/dashboards#/view/e2937420-c8ba-11ed-a7eb-3d08ee4d53cb?_g=(filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:security-solution-default,key:event.action,negate:!f,params:(query:host),type:phrase),query:(match_phrase:(event.action:host)))),query:(language:kuery,query:''),time:(from:now%2Fd,to:now%2Fd))"` - ); - }); - - it('create links to Dashboard app - with query', () => { - const testProps = { - ...props, - query: { - language: 'kuery', - query: '@timestamp : *', - }, - } as unknown as UseDashboardAppLinkProps; - - const { result } = renderHook(() => useDashboardAppLink(testProps), { wrapper: TestProviders }); - expect(result.current.href).toMatchInlineSnapshot( - `"/app/dashboards#/view/e2937420-c8ba-11ed-a7eb-3d08ee4d53cb?_g=(filters:!(),query:(language:kuery,query:'@timestamp%20:%20*'),time:(from:now%2Fd,to:now%2Fd))"` - ); - }); - - it('create links to Dashboard app - with absolute time', () => { - const testProps = { - ...props, - timeRange: { - from: '2023-03-24T00:00:00.000Z', - to: '2023-03-24T23:59:59.999Z', - }, - } as unknown as UseDashboardAppLinkProps; - - const { result } = renderHook(() => useDashboardAppLink(testProps), { wrapper: TestProviders }); - expect(result.current.href).toMatchInlineSnapshot( - `"/app/dashboards#/view/e2937420-c8ba-11ed-a7eb-3d08ee4d53cb?_g=(filters:!(),query:(language:kuery,query:''),time:(from:'2023-03-24T00:00:00.000Z',to:'2023-03-24T23:59:59.999Z'))"` - ); - }); - - it('navigate to dashboard app with preserved states', () => { - const testProps = { - ...props, - timeRange: { - from: '2023-03-24T00:00:00.000Z', - to: '2023-03-24T23:59:59.999Z', - }, - } as unknown as UseDashboardAppLinkProps; - - const { result } = renderHook(() => useDashboardAppLink(testProps), { - wrapper: TestProviders, - }); - result.current.onClick({ - preventDefault: jest.fn(), - } as unknown as React.MouseEvent); - - expect(mockNavigateTo).toHaveBeenCalledWith( - expect.objectContaining({ - url: "/app/dashboards#/view/e2937420-c8ba-11ed-a7eb-3d08ee4d53cb?_g=(filters:!(),query:(language:kuery,query:''),time:(from:'2023-03-24T00:00:00.000Z',to:'2023-03-24T23:59:59.999Z'))", - }) - ); - }); -}); diff --git a/x-pack/plugins/security_solution/public/dashboards/hooks/use_dashboard_app_link.tsx b/x-pack/plugins/security_solution/public/dashboards/hooks/use_dashboard_app_link.tsx deleted file mode 100644 index 1bf8a16f1ce19..0000000000000 --- a/x-pack/plugins/security_solution/public/dashboards/hooks/use_dashboard_app_link.tsx +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { createDashboardEditUrl, DASHBOARD_APP_ID } from '@kbn/dashboard-plugin/public'; -import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/public'; -import type { IUiSettingsClient } from '@kbn/core/public'; -import { useMemo } from 'react'; -import type { Filter, Query } from '@kbn/es-query'; -import { useNavigation } from '../../common/lib/kibana'; - -const GLOBAL_STATE_STORAGE_KEY = '_g'; - -export interface UseDashboardAppLinkProps { - query?: Query; - filters?: Filter[]; - timeRange: { - from: string; - to: string; - fromStr?: string | undefined; - toStr?: string | undefined; - }; - uiSettings: IUiSettingsClient; - savedObjectId: string | undefined; -} - -export const useDashboardAppLink = ({ - query, - filters, - timeRange: { from, fromStr, to, toStr }, - uiSettings, - savedObjectId, -}: UseDashboardAppLinkProps) => { - const { navigateTo, getAppUrl } = useNavigation(); - const useHash = uiSettings.get('state:storeInSessionStorage'); - - let editDashboardUrl = useMemo( - () => - getAppUrl({ - appId: DASHBOARD_APP_ID, - path: `#${createDashboardEditUrl(savedObjectId)}`, - }), - [getAppUrl, savedObjectId] - ); - - editDashboardUrl = setStateToKbnUrl( - GLOBAL_STATE_STORAGE_KEY, - { - time: { from: fromStr ?? from, to: toStr ?? to }, - filters, - query, - }, - { useHash, storeInHashQuery: true }, - editDashboardUrl - ); - - const editDashboardLinkProps = useMemo( - () => ({ - onClick: (ev: React.MouseEvent) => { - ev.preventDefault(); - navigateTo({ url: editDashboardUrl }); - }, - href: editDashboardUrl, - }), - [editDashboardUrl, navigateTo] - ); - return editDashboardLinkProps; -}; diff --git a/x-pack/plugins/security_solution/public/dashboards/hooks/use_dashboard_view_prompt_state.test.tsx b/x-pack/plugins/security_solution/public/dashboards/hooks/use_dashboard_view_prompt_state.test.tsx index b309d0a646a58..6fd7fb3e2de10 100644 --- a/x-pack/plugins/security_solution/public/dashboards/hooks/use_dashboard_view_prompt_state.test.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/hooks/use_dashboard_view_prompt_state.test.tsx @@ -38,20 +38,4 @@ describe('useDashboardViewPromptState', () => { } `); }); - - it('returns IndicesNotFound state', () => { - const { result } = renderHook< - DashboardViewPromptState | null, - Partial | null - >(() => useDashboardViewPromptState(DashboardViewPromptState.IndicesNotFound)); - expect(result.current).toMatchInlineSnapshot(` - Object { - "color": "danger", - "iconType": "error", - "title":

- Indices not found -

, - } - `); - }); }); diff --git a/x-pack/plugins/security_solution/public/dashboards/hooks/use_dashboard_view_prompt_state.tsx b/x-pack/plugins/security_solution/public/dashboards/hooks/use_dashboard_view_prompt_state.tsx index 20c0be416bb3d..babf644a6c4d6 100644 --- a/x-pack/plugins/security_solution/public/dashboards/hooks/use_dashboard_view_prompt_state.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/hooks/use_dashboard_view_prompt_state.tsx @@ -11,7 +11,6 @@ import * as i18n from '../pages/details/translations'; export enum DashboardViewPromptState { NoReadPermission = 'NoReadPermission', - IndicesNotFound = 'IndicesNotFound', } const dashboardViewPromptState: Record> = { @@ -21,11 +20,6 @@ const dashboardViewPromptState: Record{i18n.DASHBOARD_NO_READ_PERMISSION_TITLE}, body:

{i18n.DASHBOARD_NO_READ_PERMISSION_DESCRIPTION}

, }, - [DashboardViewPromptState.IndicesNotFound]: { - color: 'danger', - iconType: 'error', - title:

{i18n.DASHBOARD_INDICES_NOT_FOUND_TITLE}

, - }, }; export const useDashboardViewPromptState = ( diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/details/index.tsx b/x-pack/plugins/security_solution/public/dashboards/pages/details/index.tsx index f87d4b1d71f07..410906b29ae73 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/pages/details/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState, useCallback, useEffect, useMemo } from 'react'; +import React, { useState, useCallback, useMemo } from 'react'; import { LEGACY_DASHBOARD_APP_ID } from '@kbn/dashboard-plugin/public'; import type { DashboardAPI } from '@kbn/dashboard-plugin/public'; @@ -49,8 +49,9 @@ const DashboardViewComponent: React.FC = () => { const { show: canReadDashboard, showWriteControls } = useCapabilities(LEGACY_DASHBOARD_APP_ID); - const [currentState, setCurrentState] = useState( - canReadDashboard ? null : DashboardViewPromptState.NoReadPermission + const errorState = useMemo( + () => (canReadDashboard ? null : DashboardViewPromptState.NoReadPermission), + [canReadDashboard] ); const [dashboardDetails, setDashboardDetails] = useState(); const onDashboardContainerLoaded = useCallback((dashboard: DashboardAPI) => { @@ -67,12 +68,6 @@ const DashboardViewComponent: React.FC = () => { const dashboardExists = useMemo(() => dashboardDetails != null, [dashboardDetails]); const { detailName: savedObjectId } = useParams<{ detailName?: string }>(); - useEffect(() => { - if (!indicesExist) { - setCurrentState(DashboardViewPromptState.IndicesNotFound); - } - }, [indicesExist]); - return ( <> {indicesExist && ( @@ -92,7 +87,7 @@ const DashboardViewComponent: React.FC = () => { )} - {indicesExist && ( + {!errorState && ( { /> )} - + APP_DASHBOARD_LINKS); -jest.mock('../../../common/components/navigation/nav_links', () => ({ - useAppRootNavLink: () => mockAppManageLink(), +jest.mock('../../../common/links/nav_links', () => ({ + useRootNavLink: () => mockAppManageLink(), })); const CREATE_DASHBOARD_LINK = { isLoading: false, url: URL }; diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx index d6d9be365fc30..4e1e48d09793c 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx @@ -14,7 +14,7 @@ import { DashboardsTable } from '../../../common/components/dashboards/dashboard import { LandingImageCards } from '../../../landing_pages/components/landing_links_images'; import { SecurityPageName } from '../../../../common/constants'; import { useCapabilities, useNavigateTo } from '../../../common/lib/kibana'; -import { useAppRootNavLink } from '../../../common/components/navigation/nav_links'; +import { useRootNavLink } from '../../../common/links/nav_links'; import { useCreateSecurityDashboardLink } from '../../../common/containers/dashboards/use_create_security_dashboard_link'; import { Title } from '../../../common/components/header_page/title'; import { LinkButton } from '../../../common/components/links/helpers'; @@ -54,7 +54,7 @@ const Header: React.FC<{ canCreateDashboard: boolean }> = ({ canCreateDashboard }; export const DashboardsLandingPage = () => { - const dashboardLinks = useAppRootNavLink(SecurityPageName.dashboards)?.links ?? []; + const dashboardLinks = useRootNavLink(SecurityPageName.dashboards)?.links ?? []; const { show: canReadDashboard, createNew: canCreateDashboard } = useCapabilities(LEGACY_DASHBOARD_APP_ID); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/maintenance_window_callout.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/maintenance_window_callout.test.tsx index 20a8e0d1f2f94..5cd752fcf516d 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/maintenance_window_callout.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/maintenance_window_callout.test.tsx @@ -8,8 +8,12 @@ import React from 'react'; import { render, waitFor, cleanup } from '@testing-library/react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { MaintenanceWindowStatus } from '@kbn/alerting-plugin/common'; +import { + MaintenanceWindowStatus, + MAINTENANCE_WINDOW_FEATURE_ID, +} from '@kbn/alerting-plugin/common'; import type { MaintenanceWindow } from '@kbn/alerting-plugin/common'; +import { useKibana } from '../../../../common/lib/kibana'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; import { useAppToastsMock } from '../../../../common/hooks/use_app_toasts.mock'; import { MaintenanceWindowCallout } from './maintenance_window_callout'; @@ -22,6 +26,8 @@ jest.mock('./api', () => ({ fetchActiveMaintenanceWindows: jest.fn(() => Promise.resolve([])), })); +jest.mock('../../../../common/lib/kibana'); + const RUNNING_MAINTENANCE_WINDOW_1: Partial = { title: 'Maintenance window 1', id: '63057284-ac31-42ba-fe22-adfe9732e5ae', @@ -46,6 +52,9 @@ const UPCOMING_MAINTENANCE_WINDOW: Partial = { ], }; +const useKibanaMock = useKibana as jest.Mock; +const fetchActiveMaintenanceWindowsMock = fetchActiveMaintenanceWindows as jest.Mock; + describe('MaintenanceWindowCallout', () => { let appToastsMock: jest.Mocked>; @@ -54,6 +63,18 @@ describe('MaintenanceWindowCallout', () => { appToastsMock = useAppToastsMock.create(); (useAppToasts as jest.Mock).mockReturnValue(appToastsMock); + useKibanaMock.mockReturnValue({ + services: { + application: { + capabilities: { + [MAINTENANCE_WINDOW_FEATURE_ID]: { + save: true, + show: true, + }, + }, + }, + }, + }); }); afterEach(() => { @@ -62,7 +83,7 @@ describe('MaintenanceWindowCallout', () => { }); it('should be visible if currently there is at least one "running" maintenance window', async () => { - (fetchActiveMaintenanceWindows as jest.Mock).mockResolvedValue([RUNNING_MAINTENANCE_WINDOW_1]); + fetchActiveMaintenanceWindowsMock.mockResolvedValue([RUNNING_MAINTENANCE_WINDOW_1]); const { findByText } = render(, { wrapper: TestProviders }); @@ -70,7 +91,7 @@ describe('MaintenanceWindowCallout', () => { }); it('should be visible if currently there are multiple "running" maintenance windows', async () => { - (fetchActiveMaintenanceWindows as jest.Mock).mockResolvedValue([ + fetchActiveMaintenanceWindowsMock.mockResolvedValue([ RUNNING_MAINTENANCE_WINDOW_1, RUNNING_MAINTENANCE_WINDOW_2, ]); @@ -81,7 +102,7 @@ describe('MaintenanceWindowCallout', () => { }); it('should NOT be visible if currently there are no active (running or upcoming) maintenance windows', async () => { - (fetchActiveMaintenanceWindows as jest.Mock).mockResolvedValue([]); + fetchActiveMaintenanceWindowsMock.mockResolvedValue([]); const { container } = render(, { wrapper: TestProviders }); @@ -89,7 +110,7 @@ describe('MaintenanceWindowCallout', () => { }); it('should NOT be visible if currently there are no "running" maintenance windows', async () => { - (fetchActiveMaintenanceWindows as jest.Mock).mockResolvedValue([UPCOMING_MAINTENANCE_WINDOW]); + fetchActiveMaintenanceWindowsMock.mockResolvedValue([UPCOMING_MAINTENANCE_WINDOW]); const { container } = render(, { wrapper: TestProviders }); @@ -121,7 +142,7 @@ describe('MaintenanceWindowCallout', () => { }; const mockError = new Error('Network error'); - (fetchActiveMaintenanceWindows as jest.Mock).mockRejectedValue(mockError); + fetchActiveMaintenanceWindowsMock.mockRejectedValue(mockError); render(, { wrapper: createReactQueryWrapper() }); @@ -133,4 +154,44 @@ describe('MaintenanceWindowCallout', () => { }); }); }); + + it('should return null if window maintenance privilege is NONE', async () => { + useKibanaMock.mockReturnValue({ + services: { + application: { + capabilities: { + [MAINTENANCE_WINDOW_FEATURE_ID]: { + save: false, + show: false, + }, + }, + }, + }, + }); + fetchActiveMaintenanceWindowsMock.mockResolvedValue([RUNNING_MAINTENANCE_WINDOW_1]); + + const { container } = render(, { wrapper: TestProviders }); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should work as expected if window maintenance privilege is READ ', async () => { + useKibanaMock.mockReturnValue({ + services: { + application: { + capabilities: { + [MAINTENANCE_WINDOW_FEATURE_ID]: { + save: false, + show: true, + }, + }, + }, + }, + }); + fetchActiveMaintenanceWindowsMock.mockResolvedValue([RUNNING_MAINTENANCE_WINDOW_1]); + + const { findByText } = render(, { wrapper: TestProviders }); + + expect(await findByText('A maintenance window is currently running')).toBeInTheDocument(); + }); }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/maintenance_window_callout.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/maintenance_window_callout.tsx index 878347dc37c98..5a1d21b39a65b 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/maintenance_window_callout.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/maintenance_window_callout.tsx @@ -7,12 +7,28 @@ import React from 'react'; import { EuiCallOut } from '@elastic/eui'; -import { MaintenanceWindowStatus } from '@kbn/alerting-plugin/common'; +import { + MaintenanceWindowStatus, + MAINTENANCE_WINDOW_FEATURE_ID, +} from '@kbn/alerting-plugin/common'; import { useFetchActiveMaintenanceWindows } from './use_fetch_active_maintenance_windows'; import * as i18n from './translations'; +import { useKibana } from '../../../../common/lib/kibana'; export function MaintenanceWindowCallout(): JSX.Element | null { - const { data } = useFetchActiveMaintenanceWindows(); + const { + application: { capabilities }, + } = useKibana().services; + + const isMaintenanceWindowDisabled = + !capabilities[MAINTENANCE_WINDOW_FEATURE_ID].show && + !capabilities[MAINTENANCE_WINDOW_FEATURE_ID].save; + const { data } = useFetchActiveMaintenanceWindows({ enabled: !isMaintenanceWindowDisabled }); + + if (isMaintenanceWindowDisabled) { + return null; + } + const activeMaintenanceWindows = data || []; if (activeMaintenanceWindows.some(({ status }) => status === MaintenanceWindowStatus.Running)) { diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/use_fetch_active_maintenance_windows.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/use_fetch_active_maintenance_windows.ts index 3603cafbda935..69aecfa794357 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/use_fetch_active_maintenance_windows.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/maintenance_window_callout/use_fetch_active_maintenance_windows.ts @@ -5,19 +5,21 @@ * 2.0. */ +import type { UseQueryOptions } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query'; import { INTERNAL_ALERTING_API_GET_ACTIVE_MAINTENANCE_WINDOWS_PATH } from '@kbn/alerting-plugin/common'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; import * as i18n from './translations'; import { fetchActiveMaintenanceWindows } from './api'; -export const useFetchActiveMaintenanceWindows = () => { +export const useFetchActiveMaintenanceWindows = ({ enabled }: Pick) => { const { addError } = useAppToasts(); return useQuery( ['GET', INTERNAL_ALERTING_API_GET_ACTIVE_MAINTENANCE_WINDOWS_PATH], ({ signal }) => fetchActiveMaintenanceWindows(signal), { + enabled, refetchInterval: 60000, onError: (error) => { addError(error, { title: i18n.FETCH_ERROR, toastMessage: i18n.FETCH_ERROR_DESCRIPTION }); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/helpers.test.tsx index 4aa68e524f846..519a7975c5891 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/helpers.test.tsx @@ -4,9 +4,16 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import type { Action, ActionExecutionContext } from '@kbn/ui-actions-plugin/public'; +import type { Embeddable } from '@kbn/embeddable-plugin/public'; -import { formatAlertsData, showInitialLoadingSpinner } from './helpers'; +import { + createResetGroupByFieldAction, + formatAlertsData, + showInitialLoadingSpinner, +} from './helpers'; import { result, textResult, stackedByBooleanField, stackedByTextField } from './mock_data'; +import type { LensDataTableEmbeddable } from '../../../../common/components/visualization_actions/types'; describe('helpers', () => { describe('showInitialLoadingSpinner', () => { @@ -47,3 +54,103 @@ describe('formatAlertsData', () => { expect(res).toEqual(textResult); }); }); + +describe('createResetGroupByFieldAction', () => { + let action: Action; + const embeddable = { + getInput: jest.fn().mockReturnValue({ + attributes: { + title: 'test', + description: '', + visualizationType: 'lnsDatatable', + state: { + visualization: { + columns: [ + { + columnId: '2881fedd-54b7-42ba-8c97-5175dec86166', + isTransposed: false, + width: 362, + }, + { + columnId: 'f04a71a3-399f-4d32-9efc-8a005e989991', + isTransposed: false, + }, + { + columnId: '75ce269b-ee9c-4c7d-a14e-9226ba0fe059', + isTransposed: false, + hidden: true, + }, + ], + layerId: '03b95315-16ce-4146-a76a-621f9d4422f9', + layerType: 'data', + }, + }, + }, + } as unknown as Embeddable), + updateInput: jest.fn(), + }; + + const context = { + embeddable, + } as unknown as ActionExecutionContext>; + const mockCallback = jest.fn(); + beforeAll(async () => { + action = createResetGroupByFieldAction({ callback: mockCallback }); + await action.execute(context); + }); + test('should return a correct id', () => { + expect(action.id).toEqual('resetGroupByField'); + }); + + test('should return display name', () => { + expect(action.getDisplayName(context)).toEqual('Reset group by fields'); + }); + + test('should return an icon', () => { + expect(action.getIconType(context)).toEqual('editorRedo'); + }); + + test('should return icon type', () => { + expect(action.type).toEqual('actionButton'); + }); + + test('should execute callback', () => { + expect(mockCallback).toHaveBeenCalled(); + }); + + test('should unhide all the columns', () => { + expect(embeddable.updateInput).toHaveBeenCalledWith( + expect.objectContaining({ + attributes: { + description: '', + state: { + visualization: { + columns: [ + { + columnId: '2881fedd-54b7-42ba-8c97-5175dec86166', + hidden: false, + isTransposed: false, + width: 362, + }, + { + columnId: 'f04a71a3-399f-4d32-9efc-8a005e989991', + hidden: false, + isTransposed: false, + }, + { + columnId: '75ce269b-ee9c-4c7d-a14e-9226ba0fe059', + hidden: false, + isTransposed: false, + }, + ], + layerId: '03b95315-16ce-4146-a76a-621f9d4422f9', + layerType: 'data', + }, + }, + title: 'test', + visualizationType: 'lnsDatatable', + }, + }) + ); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/helpers.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/helpers.tsx index db84fc4b775bd..52b38ee048a2b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/helpers.tsx @@ -9,8 +9,12 @@ import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesW import { isEmpty } from 'lodash/fp'; import moment from 'moment'; +import type { Action, ActionExecutionContext } from '@kbn/ui-actions-plugin/public'; +import type { Embeddable } from '@kbn/embeddable-plugin/public'; import type { HistogramData, AlertsAggregation, AlertsBucket, AlertsGroupBucket } from './types'; import type { AlertSearchResponse } from '../../../containers/detection_engine/alerts/types'; +import { RESET_GROUP_BY_FIELDS } from '../../../../common/components/chart_settings_popover/configurations/default/translations'; +import type { LensDataTableEmbeddable } from '../../../../common/components/visualization_actions/types'; const EMPTY_ALERTS_DATA: HistogramData[] = []; @@ -119,3 +123,61 @@ export const buildCombinedQueries = (query?: string) => { return []; } }; + +interface CreateResetGroupByFieldActionProps { + callback?: () => void; + order?: number; +} + +type CreateResetGroupByFieldAction = (params?: CreateResetGroupByFieldActionProps) => Action; +export const createResetGroupByFieldAction: CreateResetGroupByFieldAction = ({ + callback, + order, +} = {}) => ({ + id: 'resetGroupByField', + getDisplayName(): string { + return RESET_GROUP_BY_FIELDS; + }, + getIconType(): string | undefined { + return 'editorRedo'; + }, + type: 'actionButton', + async isCompatible(): Promise { + return true; + }, + async execute({ + embeddable, + }: ActionExecutionContext<{ + embeddable: Embeddable; + }>): Promise { + callback?.(); + + const input = embeddable.getInput(); + const { + attributes: { + state: { + visualization: { columns }, + }, + }, + } = input; + + // Unhide all the columns + embeddable.updateInput({ + ...input, + attributes: { + ...input.attributes, + state: { + ...input.attributes.state, + visualization: { + ...input.attributes.state.visualization, + columns: columns.map((c) => ({ + ...c, + hidden: false, + })), + }, + }, + }, + }); + }, + order, +}); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx index eb8435d7578be..aedb45acdf3ef 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx @@ -15,12 +15,11 @@ import type { AlertsTableStateProps } from '@kbn/triggers-actions-ui-plugin/publ import type { Alert } from '@kbn/triggers-actions-ui-plugin/public/types'; import { ALERT_BUILDING_BLOCK_TYPE } from '@kbn/rule-data-utils'; import styled from 'styled-components'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import { getEsQueryConfig } from '@kbn/data-plugin/public'; import { dataTableActions, dataTableSelectors, - getColumnHeaders, tableDefaults, TableId, } from '@kbn/securitysolution-data-table'; @@ -42,12 +41,13 @@ import { getDataTablesInStorageByIds } from '../../../timelines/containers/local import { useSourcererDataView } from '../../../common/containers/sourcerer'; import { SourcererScopeName } from '../../../common/store/sourcerer/model'; import { useKibana } from '../../../common/lib/kibana'; -import { useShallowEqualSelector } from '../../../common/hooks/use_selector'; +import { useDeepEqualSelector, useShallowEqualSelector } from '../../../common/hooks/use_selector'; import { getColumns } from '../../configurations/security_solution_detections'; import { buildTimeRangeFilter } from './helpers'; import { eventsViewerSelector } from '../../../common/components/events_viewer/selectors'; import type { State } from '../../../common/store'; import * as i18n from './translations'; +import { eventRenderedViewColumns } from '../../configurations/security_solution_detections/columns'; const { updateIsLoading, updateTotalCount } = dataTableActions; @@ -96,6 +96,7 @@ interface DetectionEngineAlertTableProps { isLoading?: boolean; onRuleChange?: () => void; } + export const AlertsTableComponent: FC = ({ configId, flyoutSize, @@ -124,9 +125,13 @@ export const AlertsTableComponent: FC = ({ const { browserFields, indexPattern: indexPatterns } = useSourcererDataView(sourcererScope); const license = useLicense(); - const getGlobalInputs = inputsSelectors.globalSelector(); - const globalInputs = useSelector((state: State) => getGlobalInputs(state)); - const { query: globalQuery, filters: globalFilters } = globalInputs; + const getGlobalFiltersQuerySelector = useMemo( + () => inputsSelectors.globalFiltersQuerySelector(), + [] + ); + const getGlobalQuerySelector = useMemo(() => inputsSelectors.globalQuerySelector(), []); + const globalQuery = useDeepEqualSelector(getGlobalQuerySelector); + const globalFilters = useDeepEqualSelector(getGlobalFiltersQuerySelector); const getTable = useMemo(() => dataTableSelectors.getTableByIdSelector(), []); @@ -173,11 +178,11 @@ export const AlertsTableComponent: FC = ({ }); const finalBoolQuery: AlertsTableStateProps['query'] = useMemo(() => { - if (!combinedQuery || combinedQuery.kqlError || !combinedQuery.filterQuery) { + if (combinedQuery?.kqlError || !combinedQuery?.filterQuery) { return { bool: {} }; } - return { bool: { filter: JSON.parse(combinedQuery.filterQuery) } }; - }, [combinedQuery]); + return { bool: { filter: JSON.parse(combinedQuery?.filterQuery) } }; + }, [combinedQuery?.filterQuery, combinedQuery?.kqlError]); const isEventRenderedView = tableView === VIEW_SELECTION.eventRenderedView; @@ -205,21 +210,16 @@ export const AlertsTableComponent: FC = ({ const columnsFormStorage = dataTableStorage?.[TableId.alertsOnAlertsPage]?.columns ?? []; const alertColumns = columnsFormStorage.length ? columnsFormStorage : getColumns(license); - const evenRenderedColumns = useMemo( - () => getColumnHeaders(alertColumns, browserFields, true), - [alertColumns, browserFields] - ); - - const finalColumns = useMemo( - () => (isEventRenderedView ? evenRenderedColumns : alertColumns), - [evenRenderedColumns, alertColumns, isEventRenderedView] - ); - const finalBrowserFields = useMemo( () => (isEventRenderedView ? {} : browserFields), [isEventRenderedView, browserFields] ); + const finalColumns = useMemo( + () => (isEventRenderedView ? eventRenderedViewColumns : alertColumns), + [alertColumns, isEventRenderedView] + ); + const onAlertTableUpdate: AlertsTableStateProps['onUpdate'] = useCallback( ({ isLoading: isAlertTableLoading, totalCount, refresh }) => { dispatch( diff --git a/x-pack/plugins/security_solution/public/detections/components/detection_page_filters/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/detection_page_filters/index.test.tsx new file mode 100644 index 0000000000000..1035f08bb902f --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/detection_page_filters/index.test.tsx @@ -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 type { ComponentProps } from 'react'; +import React from 'react'; + +import { render, screen, waitFor } from '@testing-library/react'; +import { TestProviders } from '../../../common/mock'; +import { DetectionPageFilterSet } from '.'; +import { TEST_IDS } from '../../../common/components/filter_group/constants'; +import { createStartServicesMock } from '../../../common/lib/kibana/kibana_react.mock'; +import type { DataView } from '@kbn/data-views-plugin/common'; +import { useKibana } from '../../../common/lib/kibana'; +import { FilterGroup } from '../../../common/components/filter_group'; +import { getMockedFilterGroupWithCustomFilters } from '../../../common/components/filter_group/mocks'; +import type { DataViewsServicePublic } from '@kbn/data-views-plugin/public'; + +jest.mock('../../../common/components/filter_group'); + +jest.mock('../../../common/lib/kibana'); + +const basicKibanaServicesMock = createStartServicesMock(); + +const getFieldByNameMock = jest.fn(() => true); + +const mockedDataViewServiceGetter = jest.fn(() => { + return Promise.resolve({ + getFieldByName: getFieldByNameMock, + } as unknown as DataView); +}); + +const getKibanaServiceWithMockedGetter = ( + mockedDataViewGetter: DataViewsServicePublic['get'] = mockedDataViewServiceGetter +) => { + return { + ...basicKibanaServicesMock, + dataViews: { + ...basicKibanaServicesMock.dataViews, + clearInstanceCache: jest.fn(), + get: mockedDataViewGetter, + }, + }; +}; + +const kibanaServiceDefaultMock = getKibanaServiceWithMockedGetter(); + +const onFilterChangeMock = jest.fn(); + +const TestComponent = (props: Partial> = {}) => { + return ( + + + + ); +}; + +describe('Detection Page Filters', () => { + beforeAll(() => { + (FilterGroup as jest.Mock).mockImplementation(getMockedFilterGroupWithCustomFilters()); + (useKibana as jest.Mock).mockReturnValue({ + services: { + ...kibanaServiceDefaultMock, + }, + }); + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + it('should renders correctly', async () => { + render(); + expect(screen.getByTestId(TEST_IDS.FILTER_LOADING)).toBeInTheDocument(); + await waitFor(() => { + expect(screen.getByTestId(TEST_IDS.MOCKED_CONTROL)).toBeInTheDocument(); + }); + }); + + it('should check all the fields till any absent field is found', async () => { + render(); + expect(screen.getByTestId(TEST_IDS.FILTER_LOADING)).toBeInTheDocument(); + await waitFor(() => { + expect(getFieldByNameMock).toHaveBeenCalledTimes(4); + expect(kibanaServiceDefaultMock.dataViews.clearInstanceCache).toHaveBeenCalledTimes(0); + }); + }); + + it('should stop checking fields if blank field is found and clear the cache', async () => { + const getFieldByNameLocalMock = jest.fn(() => false); + const mockGetter = jest.fn(() => + Promise.resolve({ getFieldByName: getFieldByNameLocalMock } as unknown as DataView) + ); + const modifiedKibanaServicesMock = getKibanaServiceWithMockedGetter(mockGetter); + (useKibana as jest.Mock).mockReturnValueOnce({ services: modifiedKibanaServicesMock }); + + render(); + expect(screen.getByTestId(TEST_IDS.FILTER_LOADING)).toBeInTheDocument(); + await waitFor(() => { + expect(getFieldByNameLocalMock).toHaveBeenCalledTimes(1); + expect(modifiedKibanaServicesMock.dataViews.clearInstanceCache).toHaveBeenCalledTimes(1); + expect(screen.getByTestId(TEST_IDS.MOCKED_CONTROL)).toBeInTheDocument(); + }); + }); + + it('should call onFilterChange', async () => { + const filtersToTest = [ + { + meta: { + index: 'security-solution-default', + key: 'kibana.alert.severity', + }, + query: { + match_phrase: { + 'kibana.alert.severity': 'low', + }, + }, + }, + ]; + (FilterGroup as jest.Mock).mockImplementationOnce( + getMockedFilterGroupWithCustomFilters(filtersToTest) + ); + render(); + + await waitFor(() => { + expect(onFilterChangeMock).toHaveBeenNthCalledWith(1, [ + { + ...filtersToTest[0], + meta: { + ...filtersToTest[0].meta, + disabled: false, + }, + }, + ]); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/components/detection_page_filters/index.tsx b/x-pack/plugins/security_solution/public/detections/components/detection_page_filters/index.tsx index 1454c63ffb398..d096f56f7faa5 100644 --- a/x-pack/plugins/security_solution/public/detections/components/detection_page_filters/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/detection_page_filters/index.tsx @@ -41,8 +41,8 @@ const FilterItemSetComponent = (props: FilterItemSetProps) => { setLoadingPageFilters(false); return; } - setLoadingPageFilters(false); } + setLoadingPageFilters(false); })(); }, [dataViewId, dataViewService]); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx index 06368eadc30df..938d8a8a6411f 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx @@ -40,6 +40,6 @@ export const RULE_SNOOZE_DESCRIPTION = i18n.translate( export const SNOOZED_ACTIONS_WARNING = i18n.translate( 'xpack.securitySolution.detectionEngine.createRule.stepRuleActions.snoozedActionsWarning', { - defaultMessage: 'Actions will not be preformed until it is unsnoozed.', + defaultMessage: 'Actions will not be performed until it is unsnoozed.', } ); diff --git a/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/mock/data.ts b/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/mock/data.ts new file mode 100644 index 0000000000000..45637d28cd9ae --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/mock/data.ts @@ -0,0 +1,1696 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 browserFields = { + host: { + fields: { + 'host.name': { + category: 'host', + description: + 'Name of the host. It can contain what `hostname` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use.', + name: 'host.name', + type: 'string', + searchable: true, + aggregatable: true, + format: 'string', + indexes: [ + 'apm-*-transaction*', + 'traces-apm*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + }, + }, + }, +}; + +export const columns = [ + { + columnHeaderType: 'not-filtered', + displayAsText: 'Host Name', + id: 'host.name', + initialWidth: 30, + }, +]; + +export const data = [ + [ + { + field: 'kibana.alert.rule.updated_by', + value: ['elastic'], + }, + { + field: 'host.os.name.text', + value: ['Debian GNU/Linux'], + }, + { + field: 'host.hostname', + value: ['bastion00'], + }, + { + field: 'host.mac', + value: ['42-01-0A-C8-00-2E'], + }, + { + field: 'service.type', + value: ['system'], + }, + { + field: 'signal.rule.enabled', + value: ['true'], + }, + { + field: 'host.os.version', + value: ['10 (buster)'], + }, + { + field: 'signal.rule.max_signals', + value: [100], + }, + { + field: 'source.geo.region_name', + value: ['Iowa'], + }, + { + field: 'signal.rule.updated_at', + value: ['2023-04-25T09:03:13.384Z'], + }, + { + field: 'kibana.alert.risk_score', + value: [21], + }, + { + field: 'source.geo.city_name', + value: ['Council Bluffs'], + }, + { + field: 'host.os.type', + value: ['linux'], + }, + { + field: 'signal.original_event.end', + value: ['2022-10-14T14:00:24.296Z'], + }, + { + field: 'kibana.alert.original_event.module', + value: ['system'], + }, + { + field: 'kibana.alert.rule.interval', + value: ['5m'], + }, + { + field: 'kibana.alert.rule.type', + value: ['query'], + }, + { + field: 'agent.hostname', + value: ['bastion00.siem.estc.dev'], + }, + { + field: 'kibana.alert.original_event.end', + value: ['2022-10-14T14:00:24.296Z'], + }, + { + field: 'kibana.alert.rule.immutable', + value: ['false'], + }, + { + field: 'source.port', + value: [56296], + }, + { + field: 'client.port', + value: [56296], + }, + { + field: 'host.containerized', + value: [false], + }, + { + field: 'destination.bytes', + value: [6564], + }, + { + field: 'kibana.alert.rule.version', + value: ['1'], + }, + { + field: 'source.as.number', + value: [396982], + }, + { + field: 'event.end', + value: ['2022-10-14T14:00:24.296Z'], + }, + { + field: 'process.entity_id', + value: ['rR78V6MnxUY7Go9O'], + }, + { + field: 'host.ip', + value: ['10.200.0.46', 'fe80::4001:aff:fec8:2e'], + }, + { + field: 'agent.type', + value: ['auditbeat'], + }, + { + field: 'signal.original_event.category', + value: ['network'], + }, + { + field: 'process.executable.text', + value: ['/usr/local/bin/traefik'], + }, + { + field: 'related.ip', + value: ['10.200.0.46', '35.226.77.71'], + }, + { + field: 'server.port', + value: [9200], + }, + { + field: 'host.id', + value: ['a415f7ec8a9af33894317958f24e185f'], + }, + { + field: 'timestamp', + value: [1666873459714], + }, + { + field: 'signal.rule.building_block_type', + value: ['default'], + }, + { + field: 'kibana.alert.rule.indices', + value: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'traces-apm*', + 'winlogbeat-*', + '-*elastic-cloud-logs-*', + ], + }, + { + field: 'signal.rule.updated_by', + value: ['elastic'], + }, + { + field: 'cloud.account.id', + value: ['elastic-siem'], + }, + { + field: 'host.os.platform', + value: ['debian'], + }, + { + field: 'kibana.alert.rule.severity', + value: ['low'], + }, + { + field: 'signal.original_event.duration', + value: ['647874250'], + }, + { + field: 'kibana.version', + value: ['8.8.0'], + }, + { + field: 'signal.ancestors.type', + value: ['event', 'signal'], + }, + { + field: 'cloud.instance.name', + value: ['bastion00'], + }, + { + field: 'user.name.text', + value: ['traefik'], + }, + { + field: 'kibana.alert.ancestors.id', + value: [ + '4z_L1oMBiIcG4Y9J2nxd', + '75e2a3903daac549c05eb3aebe90b3ef6d1921b74c4870ee518643a46f778a31', + ], + }, + { + field: 'kibana.alert.rule.building_block_type', + value: ['default'], + }, + { + field: 'process.name.text', + value: ['traefik'], + }, + { + field: 'kibana.alert.rule.description', + value: ['Custom Rule'], + }, + { + field: 'kibana.alert.rule.producer', + value: ['siem'], + }, + { + field: 'kibana.alert.rule.to', + value: ['now'], + }, + { + field: 'signal.rule.id', + value: ['023a0260-e348-11ed-bd43-632e972150d6'], + }, + { + field: 'system.audit.socket.uid', + value: [1018], + }, + { + field: 'signal.rule.risk_score', + value: [21], + }, + { + field: 'signal.reason', + value: [ + 'network event with process traefik, source 35.226.77.71:56296, destination 10.200.0.46:9200, by traefik on bastion00.siem.estc.dev created low alert Custom Rule.', + ], + }, + { + field: 'process.created', + value: ['2022-01-31T13:14:05.210Z'], + }, + { + field: 'user.risk.calculated_score_norm', + value: [31.238997], + }, + { + field: 'host.os.name', + value: ['Debian GNU/Linux'], + }, + { + field: 'signal.status', + value: ['open'], + }, + { + field: 'client.as.organization.name.text', + value: ['GOOGLE-CLOUD-PLATFORM'], + }, + { + field: 'flow.final', + value: [true], + }, + { + field: 'kibana.alert.rule.uuid', + value: ['023a0260-e348-11ed-bd43-632e972150d6'], + }, + { + field: 'kibana.alert.original_event.category', + value: ['network'], + }, + { + field: 'client.ip', + value: ['35.226.77.71'], + }, + { + field: 'process.name', + value: ['traefik'], + }, + { + field: 'client.as.number', + value: [396982], + }, + { + field: 'kibana.alert.ancestors.index', + value: ['.ds-auditbeat-8.5.0-2022.10.03-000001', 'auditbeat-bulk'], + }, + { + field: 'agent.version', + value: ['8.5.0'], + }, + { + field: 'host.os.family', + value: ['debian'], + }, + { + field: 'kibana.alert.rule.from', + value: ['now-6000000300s'], + }, + { + field: 'kibana.alert.rule.parameters', + value: [ + { + description: 'Custom Rule', + risk_score: 21, + severity: 'low', + building_block_type: 'default', + license: '', + meta: { + from: '100000000m', + kibana_siem_app_url: 'http://localhost:5601/app/security', + }, + author: [], + false_positives: [], + from: 'now-6000000300s', + rule_id: 'caf0db0c-c7e2-455e-89b8-6b0d73619ae5', + max_signals: 100, + risk_score_mapping: [], + severity_mapping: [], + threat: [], + to: 'now', + references: [], + version: 1, + exceptions_list: [], + immutable: false, + related_integrations: [], + required_fields: [], + setup: '', + type: 'query', + language: 'kuery', + index: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'traces-apm*', + 'winlogbeat-*', + '-*elastic-cloud-logs-*', + ], + query: '*', + filters: [], + }, + ], + }, + { + field: 'signal.original_event.kind', + value: ['signal'], + }, + { + field: 'client.packets', + value: [10], + }, + { + field: 'group.id', + value: ['1019'], + }, + { + field: 'signal.depth', + value: [2], + }, + { + field: 'kibana.alert.original_event.start', + value: ['2022-10-14T14:00:23.649Z'], + }, + { + field: 'signal.rule.immutable', + value: ['false'], + }, + { + field: 'signal.rule.name', + value: ['Custom Rule'], + }, + { + field: 'event.module', + value: ['system'], + }, + { + field: 'host.os.kernel', + value: ['4.19.0-17-cloud-amd64'], + }, + { + field: 'kibana.alert.rule.license', + value: [''], + }, + { + field: 'kibana.alert.original_event.kind', + value: ['signal'], + }, + { + field: 'network.type', + value: ['ipv4'], + }, + { + field: 'source.as.organization.name.text', + value: ['GOOGLE-CLOUD-PLATFORM'], + }, + { + field: 'signal.rule.description', + value: ['Custom Rule'], + }, + { + field: 'source.geo.continent_name', + value: ['North America'], + }, + { + field: 'source.as.organization.name', + value: ['GOOGLE-CLOUD-PLATFORM'], + }, + { + field: 'process.args', + value: ['/usr/local/bin/traefik', '--configfile=/etc/traefik/traefik.yml'], + }, + { + field: 'client.bytes', + value: [1495], + }, + { + field: 'kibana.space_ids', + value: ['default'], + }, + { + field: 'flow.complete', + value: [true], + }, + { + field: 'kibana.alert.severity', + value: ['low'], + }, + { + field: 'signal.ancestors.depth', + value: [0, 1], + }, + { + field: 'event.category', + value: ['network'], + }, + { + field: 'host.risk.calculated_score_norm', + value: [17.08461], + }, + { + field: 'client.geo.country_iso_code', + value: ['US'], + }, + { + field: 'kibana.alert.ancestors.depth', + value: [0, 1], + }, + { + field: 'source.ip', + value: ['35.226.77.71'], + }, + { + field: 'agent.name', + value: ['bastion00.siem.estc.dev'], + }, + { + field: 'network.community_id', + value: ['1:IjFQUo1K34NyjD8pxAitVL9mqdw='], + }, + { + field: 'system.audit.socket.euid', + value: [1018], + }, + { + field: 'group.name', + value: ['traefik'], + }, + { + field: 'source.packets', + value: [10], + }, + { + field: 'user.id', + value: ['1018'], + }, + { + field: 'system.audit.socket.gid', + value: [1019], + }, + { + field: 'related.user', + value: ['traefik'], + }, + { + field: 'host.architecture', + value: ['x86_64'], + }, + { + field: 'kibana.alert.start', + value: ['2023-04-25T09:03:15.535Z'], + }, + { + field: 'cloud.provider', + value: ['gcp'], + }, + { + field: 'cloud.machine.type', + value: ['e2-small'], + }, + { + field: 'kibana.alert.original_event.type', + value: ['info', 'connection'], + }, + { + field: 'signal.original_event.module', + value: ['system'], + }, + { + field: 'agent.id', + value: ['bb1e5c8f-10ba-48b0-bee1-da60886ba59c'], + }, + { + field: 'signal.rule.from', + value: ['now-6000000300s'], + }, + { + field: 'kibana.alert.rule.enabled', + value: ['true'], + }, + { + field: 'signal.original_event.start', + value: ['2022-10-14T14:00:23.649Z'], + }, + { + field: 'event.start', + value: ['2022-10-14T14:00:23.649Z'], + }, + { + field: 'kibana.alert.ancestors.type', + value: ['event', 'signal'], + }, + { + field: 'destination.port', + value: [9200], + }, + { + field: 'user.name', + value: ['traefik'], + }, + { + field: 'signal.ancestors.index', + value: ['.ds-auditbeat-8.5.0-2022.10.03-000001', 'auditbeat-bulk'], + }, + { + field: 'destination.packets', + value: [9], + }, + { + field: 'kibana.alert.original_event.duration', + value: ['647874250'], + }, + { + field: 'cloud.instance.id', + value: ['2730026390719231438'], + }, + { + field: 'client.geo.region_name', + value: ['Iowa'], + }, + { + field: 'signal.original_event.type', + value: ['info', 'connection'], + }, + { + field: 'kibana.alert.rule.max_signals', + value: [100], + }, + { + field: 'kibana.alert.rule.risk_score', + value: [21], + }, + { + field: 'host.os.codename', + value: ['buster'], + }, + { + field: 'signal.original_event.dataset', + value: ['socket'], + }, + { + field: 'kibana.alert.rule.consumer', + value: ['siem'], + }, + { + field: 'destination.ip', + value: ['10.200.0.46'], + }, + { + field: 'kibana.alert.rule.category', + value: ['Custom Query Rule'], + }, + { + field: 'event.duration', + value: [647874250], + }, + { + field: 'event.action', + value: ['exec'], + }, + { + field: '@timestamp', + value: ['2023-04-25T09:03:15.516Z'], + }, + { + field: 'kibana.alert.original_event.action', + value: ['exec'], + }, + { + field: 'host.risk.calculated_level', + value: ['Unknown'], + }, + { + field: 'agent.ephemeral_id', + value: ['71eae441-3269-4828-9496-99a6fd42cb91'], + }, + { + field: 'kibana.alert.rule.execution.uuid', + value: ['3e763264-9eab-4c6b-98e8-d51367491275'], + }, + { + field: 'kibana.alert.uuid', + value: ['7d3194c7158fb9edb10541ccdd81f8c2b9c9d3c50b82c14457c959a2fcbcb22a'], + }, + { + field: 'kibana.alert.rule.meta.kibana_siem_app_url', + value: ['http://localhost:5601/app/security'], + }, + { + field: 'signal.rule.license', + value: [''], + }, + { + field: 'kibana.alert.rule.rule_id', + value: ['caf0db0c-c7e2-455e-89b8-6b0d73619ae5'], + }, + { + field: 'kibana.alert.ancestors.rule', + value: ['532e0020-4a0c-11ed-9aa3-574e520c127d'], + }, + { + field: 'cloud.project.id', + value: ['elastic-siem'], + }, + { + field: 'signal.rule.type', + value: ['query'], + }, + { + field: 'server.ip', + value: ['10.200.0.46'], + }, + { + field: 'user.risk.calculated_level', + value: ['Low'], + }, + { + field: 'kibana.alert.url', + value: [ + 'http://localhost:5601/app/security/alerts/redirect/7d3194c7158fb9edb10541ccdd81f8c2b9c9d3c50b82c14457c959a2fcbcb22a?index=.alerts-security.alerts-default×tamp=2023-04-25T09:03:15.516Z', + ], + }, + { + field: 'kibana.alert.building_block_type', + value: ['default'], + }, + { + field: 'process.pid', + value: [31349], + }, + { + field: 'system.audit.socket.egid', + value: [1019], + }, + { + field: 'signal.rule.interval', + value: ['5m'], + }, + { + field: 'cloud.availability_zone', + value: ['us-central1-c'], + }, + { + field: 'signal.rule.created_by', + value: ['elastic'], + }, + { + field: 'kibana.alert.rule.created_by', + value: ['elastic'], + }, + { + field: 'system.audit.socket.kernel_sock_address', + value: ['0xffff97d5d9608900'], + }, + { + field: 'kibana.alert.rule.name', + value: ['Custom Rule'], + }, + { + field: 'client.as.organization.name', + value: ['GOOGLE-CLOUD-PLATFORM'], + }, + { + field: 'host.name', + value: ['bastion00.siem.estc.dev'], + }, + { + field: 'client.geo.country_name', + value: ['United States'], + }, + { + field: 'source.geo.region_iso_code', + value: ['US-IA'], + }, + { + field: 'event.kind', + value: ['signal'], + }, + { + field: 'signal.rule.created_at', + value: ['2023-04-25T09:03:13.384Z'], + }, + { + field: 'kibana.alert.workflow_status', + value: ['open'], + }, + { + field: 'network.packets', + value: [19], + }, + { + field: 'kibana.alert.reason', + value: [ + 'network event with process traefik, source 35.226.77.71:56296, destination 10.200.0.46:9200, by traefik on bastion00.siem.estc.dev created low alert Custom Rule.', + ], + }, + { + field: 'signal.ancestors.id', + value: [ + '4z_L1oMBiIcG4Y9J2nxd', + '75e2a3903daac549c05eb3aebe90b3ef6d1921b74c4870ee518643a46f778a31', + ], + }, + { + field: 'signal.original_time', + value: ['2022-10-27T12:24:19.714Z'], + }, + { + field: 'cloud.service.name', + value: ['GCE'], + }, + { + field: 'ecs.version', + value: ['8.0.0'], + }, + { + field: 'signal.rule.severity', + value: ['low'], + }, + { + field: 'kibana.alert.depth', + value: [2], + }, + { + field: 'kibana.alert.rule.revision', + value: [0], + }, + { + field: 'signal.rule.version', + value: ['1'], + }, + { + field: 'client.geo.continent_name', + value: ['North America'], + }, + { + field: 'server.bytes', + value: [6564], + }, + { + field: 'kibana.alert.status', + value: ['active'], + }, + { + field: 'kibana.alert.last_detected', + value: ['2023-04-25T09:03:15.535Z'], + }, + { + field: 'kibana.alert.original_event.dataset', + value: ['socket'], + }, + { + field: 'kibana.alert.rule.rule_type_id', + value: ['siem.queryRule'], + }, + { + field: 'signal.rule.rule_id', + value: ['caf0db0c-c7e2-455e-89b8-6b0d73619ae5'], + }, + { + field: 'source.geo.country_iso_code', + value: ['US'], + }, + { + field: 'network.bytes', + value: [8059], + }, + { + field: 'network.direction', + value: ['ingress'], + }, + { + field: 'process.executable', + value: ['/usr/local/bin/traefik'], + }, + { + field: 'source.bytes', + value: [1495], + }, + { + field: 'client.geo.city_name', + value: ['Council Bluffs'], + }, + { + field: 'client.geo.region_iso_code', + value: ['US-IA'], + }, + { + field: 'kibana.alert.rule.updated_at', + value: ['2023-04-25T09:03:13.384Z'], + }, + { + field: 'server.packets', + value: [9], + }, + { + field: 'network.transport', + value: ['tcp'], + }, + { + field: 'signal.original_event.action', + value: ['exec'], + }, + { + field: 'kibana.alert.rule.created_at', + value: ['2023-04-25T09:03:13.384Z'], + }, + { + field: 'signal.rule.to', + value: ['now'], + }, + { + field: 'event.type', + value: ['info', 'connection'], + }, + { + field: 'process.entry_leader.entity_id', + value: ['some99'], + }, + { + field: 'source.geo.country_name', + value: ['United States'], + }, + { + field: 'kibana.alert.rule.meta.from', + value: ['100000000m'], + }, + { + field: 'event.dataset', + value: ['socket'], + }, + { + field: 'kibana.alert.original_time', + value: ['2022-10-27T12:24:19.714Z'], + }, + { + field: '_id', + value: '7d3194c7158fb9edb10541ccdd81f8c2b9c9d3c50b82c14457c959a2fcbcb22a', + }, + { + field: '_index', + value: '.internal.alerts-security.alerts-default-000001', + }, + ], + [ + { + field: 'kibana.alert.severity', + value: ['low'], + }, + { + field: 'host.os.full.text', + value: ['Windows Server 2019 Datacenter 1809 (10.0.17763.3406)'], + }, + { + field: 'kibana.alert.rule.updated_by', + value: ['elastic'], + }, + { + field: 'signal.ancestors.depth', + value: [0, 1], + }, + { + field: 'event.category', + value: ['file'], + }, + { + field: 'host.risk.calculated_score_norm', + value: [31.092354], + }, + { + field: 'host.os.name.text', + value: ['Windows'], + }, + { + field: 'process.parent.pid', + value: [652], + }, + { + field: 'host.hostname', + value: ['siem-windows-endpoint'], + }, + { + field: 'signal.original_event.created', + value: ['2022-10-14T14:00:27.258Z'], + }, + { + field: 'host.mac', + value: ['42:01:0a:c8:00:df'], + }, + { + field: 'process.code_signature.exists', + value: [true], + }, + { + field: 'elastic.agent.id', + value: ['2ea4b363-6a3f-449e-9d4e-c73ccf28f693'], + }, + { + field: 'kibana.alert.ancestors.depth', + value: [0, 1], + }, + { + field: 'signal.rule.enabled', + value: ['true'], + }, + { + field: 'host.os.version', + value: ['1809 (10.0.17763.3406)'], + }, + { + field: 'signal.rule.max_signals', + value: [100], + }, + { + field: 'signal.rule.updated_at', + value: ['2023-04-25T09:03:13.384Z'], + }, + { + field: 'kibana.alert.risk_score', + value: [21], + }, + { + field: 'event.agent_id_status', + value: ['verified'], + }, + { + field: 'kibana.alert.original_event.id', + value: ['MnfiZLkz1DywMGBf++++9ApC'], + }, + { + field: 'file.path.text', + value: ['C:\\ProgramData\\winlogbeat\\.winlogbeat.yml.new'], + }, + { + field: 'host.os.type', + value: ['windows'], + }, + { + field: 'user.id', + value: ['S-1-5-18'], + }, + { + field: 'process.Ext.ancestry', + value: [ + 'MmVhNGIzNjMtNmEzZi00NDllLTlkNGUtYzczY2NmMjhmNjkzLTY1Mi0xNjY0ODA2NTI4Ljc4NTg5NTAw', + 'MmVhNGIzNjMtNmEzZi00NDllLTlkNGUtYzczY2NmMjhmNjkzLTUzNi0xNjY0ODA2NTI3LjEwNDI2NTQwMA==', + ], + }, + { + field: 'kibana.alert.original_event.module', + value: ['endpoint'], + }, + { + field: 'kibana.alert.rule.interval', + value: ['5m'], + }, + { + field: 'kibana.alert.rule.type', + value: ['query'], + }, + { + field: 'signal.original_event.sequence', + value: [940441], + }, + { + field: 'host.architecture', + value: ['x86_64'], + }, + { + field: 'kibana.alert.start', + value: ['2023-04-25T09:03:15.535Z'], + }, + { + field: 'kibana.alert.rule.immutable', + value: ['false'], + }, + { + field: 'process.Ext.code_signature.status', + value: ['trusted'], + }, + { + field: 'kibana.alert.original_event.type', + value: ['creation'], + }, + { + field: 'signal.original_event.module', + value: ['endpoint'], + }, + { + field: 'agent.id', + value: ['2ea4b363-6a3f-449e-9d4e-c73ccf28f693'], + }, + { + field: 'signal.rule.from', + value: ['now-6000000300s'], + }, + { + field: 'kibana.alert.rule.enabled', + value: ['true'], + }, + { + field: 'kibana.alert.rule.version', + value: ['1'], + }, + { + field: 'kibana.alert.ancestors.type', + value: ['event', 'signal'], + }, + { + field: 'file.Ext.windows.zone_identifier', + value: [-1], + }, + { + field: 'user.name', + value: ['SYSTEM'], + }, + { + field: 'signal.ancestors.index', + value: ['.ds-logs-endpoint.events.file-default-2022.10.08-000003', 'auditbeat-bulk'], + }, + { + field: 'process.entity_id', + value: [ + 'MmVhNGIzNjMtNmEzZi00NDllLTlkNGUtYzczY2NmMjhmNjkzLTE3MDQtMTY2NDgwNjcwNi4yODUzODk4MDA=', + ], + }, + { + field: 'host.ip', + value: ['10.200.0.223', 'fe80::eda9:848c:24ae:431d', '127.0.0.1', '::1'], + }, + { + field: 'agent.type', + value: ['endpoint'], + }, + { + field: 'signal.original_event.category', + value: ['file'], + }, + { + field: 'process.executable.text', + value: ['C:\\Program Files\\Winlogbeat\\winlogbeat.exe'], + }, + { + field: 'signal.original_event.id', + value: ['MnfiZLkz1DywMGBf++++9ApC'], + }, + { + field: 'user.domain', + value: ['NT AUTHORITY'], + }, + { + field: 'host.id', + value: ['526e76a2-1c82-4245-a179-4fcde1e608fc'], + }, + { + field: 'process.Ext.code_signature.subject_name', + value: ['Elasticsearch, Inc.'], + }, + { + field: 'timestamp', + value: [1666873459714], + }, + { + field: 'signal.original_event.type', + value: ['creation'], + }, + { + field: 'kibana.alert.rule.max_signals', + value: [100], + }, + { + field: 'kibana.alert.rule.risk_score', + value: [21], + }, + { + field: 'file.name', + value: ['.winlogbeat.yml.new'], + }, + { + field: 'signal.rule.building_block_type', + value: ['default'], + }, + { + field: 'process.code_signature.status', + value: ['trusted'], + }, + { + field: 'signal.original_event.dataset', + value: ['endpoint.events.file'], + }, + { + field: 'kibana.alert.rule.consumer', + value: ['siem'], + }, + { + field: 'kibana.alert.rule.indices', + value: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'traces-apm*', + 'winlogbeat-*', + '-*elastic-cloud-logs-*', + ], + }, + { + field: 'kibana.alert.rule.category', + value: ['Custom Query Rule'], + }, + { + field: 'host.os.Ext.variant', + value: ['Windows Server 2019 Datacenter'], + }, + { + field: 'event.action', + value: ['exec'], + }, + { + field: 'event.ingested', + value: ['2022-10-14T14:00:50.000Z'], + }, + { + field: '@timestamp', + value: ['2023-04-25T09:03:15.515Z'], + }, + { + field: 'kibana.alert.original_event.action', + value: ['exec'], + }, + { + field: 'signal.rule.updated_by', + value: ['elastic'], + }, + { + field: 'host.os.platform', + value: ['windows'], + }, + { + field: 'kibana.alert.rule.severity', + value: ['low'], + }, + { + field: 'kibana.alert.original_event.agent_id_status', + value: ['verified'], + }, + { + field: 'data_stream.dataset', + value: ['endpoint.events.file'], + }, + { + field: 'host.risk.calculated_level', + value: ['Low'], + }, + { + field: 'kibana.alert.rule.execution.uuid', + value: ['3e763264-9eab-4c6b-98e8-d51367491275'], + }, + { + field: 'kibana.alert.uuid', + value: ['1066d5c3675d7eef4b9e84af64e795be39d1896ec791049eb87a39eb9675a2aa'], + }, + { + field: 'kibana.alert.rule.meta.kibana_siem_app_url', + value: ['http://localhost:5601/app/security'], + }, + { + field: 'kibana.version', + value: ['8.8.0'], + }, + { + field: 'event.id', + value: ['MnfiZLkz1DywMGBf++++9ApC'], + }, + { + field: 'signal.rule.license', + value: [''], + }, + { + field: 'signal.ancestors.type', + value: ['event', 'signal'], + }, + { + field: 'kibana.alert.rule.rule_id', + value: ['caf0db0c-c7e2-455e-89b8-6b0d73619ae5'], + }, + { + field: 'kibana.alert.ancestors.rule', + value: ['532e0020-4a0c-11ed-9aa3-574e520c127d'], + }, + { + field: 'user.name.text', + value: ['SYSTEM'], + }, + { + field: 'file.path', + value: ['C:\\ProgramData\\winlogbeat\\.winlogbeat.yml.new'], + }, + { + field: 'signal.rule.type', + value: ['query'], + }, + { + field: 'kibana.alert.ancestors.id', + value: [ + 'cyjM1oMBw7Pvz6uQMxM4', + 'd46d9fecad3a03741575f552c994ec9c9e5d1089557d49d125255bcd8d863a99', + ], + }, + { + field: 'kibana.alert.rule.building_block_type', + value: ['default'], + }, + { + field: 'process.name.text', + value: ['winlogbeat.exe'], + }, + { + field: 'host.os.full', + value: ['Windows Server 2019 Datacenter 1809 (10.0.17763.3406)'], + }, + { + field: 'user.risk.calculated_level', + value: ['High'], + }, + { + field: 'kibana.alert.url', + value: [ + 'http://localhost:5601/app/security/alerts/redirect/1066d5c3675d7eef4b9e84af64e795be39d1896ec791049eb87a39eb9675a2aa?index=.alerts-security.alerts-default×tamp=2023-04-25T09:03:15.515Z', + ], + }, + { + field: 'kibana.alert.rule.description', + value: ['Custom Rule'], + }, + { + field: 'kibana.alert.building_block_type', + value: ['default'], + }, + { + field: 'process.pid', + value: [1704], + }, + { + field: 'kibana.alert.rule.producer', + value: ['siem'], + }, + { + field: 'signal.rule.interval', + value: ['5m'], + }, + { + field: 'signal.rule.created_by', + value: ['elastic'], + }, + { + field: 'kibana.alert.rule.to', + value: ['now'], + }, + { + field: 'kibana.alert.rule.created_by', + value: ['elastic'], + }, + { + field: 'kibana.alert.original_event.ingested', + value: ['2022-10-14T14:00:50.000Z'], + }, + { + field: 'signal.rule.id', + value: ['023a0260-e348-11ed-bd43-632e972150d6'], + }, + { + field: 'process.code_signature.subject_name', + value: ['Elasticsearch, Inc.'], + }, + { + field: 'signal.rule.risk_score', + value: [21], + }, + { + field: 'signal.reason', + value: [ + 'file event with process winlogbeat.exe, file .winlogbeat.yml.new, by SYSTEM on siem-windows-endpoint created low alert Custom Rule.', + ], + }, + { + field: 'user.risk.calculated_score_norm', + value: [75.22127], + }, + { + field: 'host.os.name', + value: ['Windows'], + }, + { + field: 'kibana.alert.rule.name', + value: ['Custom Rule'], + }, + { + field: 'host.name', + value: ['siem-windows-endpoint'], + }, + { + field: 'signal.status', + value: ['open'], + }, + { + field: 'event.kind', + value: ['signal'], + }, + { + field: 'process.code_signature.trusted', + value: [true], + }, + { + field: 'signal.rule.created_at', + value: ['2023-04-25T09:03:13.384Z'], + }, + { + field: 'kibana.alert.workflow_status', + value: ['open'], + }, + { + field: 'kibana.alert.original_event.created', + value: ['2022-10-14T14:00:27.258Z'], + }, + { + field: 'kibana.alert.rule.uuid', + value: ['023a0260-e348-11ed-bd43-632e972150d6'], + }, + { + field: 'kibana.alert.original_event.category', + value: ['file'], + }, + { + field: 'kibana.alert.reason', + value: [ + 'file event with process winlogbeat.exe, file .winlogbeat.yml.new, by SYSTEM on siem-windows-endpoint created low alert Custom Rule.', + ], + }, + { + field: 'data_stream.type', + value: ['logs'], + }, + { + field: 'signal.ancestors.id', + value: [ + 'cyjM1oMBw7Pvz6uQMxM4', + 'd46d9fecad3a03741575f552c994ec9c9e5d1089557d49d125255bcd8d863a99', + ], + }, + { + field: 'signal.original_time', + value: ['2022-10-27T12:24:19.714Z'], + }, + { + field: 'process.name', + value: ['winlogbeat.exe'], + }, + { + field: 'file.Ext.header_bytes', + value: ['7570646174655f74696d653a20323032'], + }, + { + field: 'ecs.version', + value: ['1.11.0'], + }, + { + field: 'signal.rule.severity', + value: ['low'], + }, + { + field: 'kibana.alert.ancestors.index', + value: ['.ds-logs-endpoint.events.file-default-2022.10.08-000003', 'auditbeat-bulk'], + }, + { + field: 'event.created', + value: ['2022-10-14T14:00:27.258Z'], + }, + { + field: 'process.Ext.code_signature.trusted', + value: [true], + }, + { + field: 'file.extension', + value: ['new'], + }, + { + field: 'agent.version', + value: ['8.5.0-SNAPSHOT'], + }, + { + field: 'kibana.alert.depth', + value: [2], + }, + { + field: 'process.thread.id', + value: [4100], + }, + { + field: 'host.os.family', + value: ['windows'], + }, + { + field: 'kibana.alert.rule.from', + value: ['now-6000000300s'], + }, + { + field: 'kibana.alert.rule.revision', + value: [0], + }, + { + field: 'kibana.alert.rule.parameters', + value: [ + { + description: 'Custom Rule', + risk_score: 21, + severity: 'low', + building_block_type: 'default', + license: '', + meta: { + from: '100000000m', + kibana_siem_app_url: 'http://localhost:5601/app/security', + }, + author: [], + false_positives: [], + from: 'now-6000000300s', + rule_id: 'caf0db0c-c7e2-455e-89b8-6b0d73619ae5', + max_signals: 100, + risk_score_mapping: [], + severity_mapping: [], + threat: [], + to: 'now', + references: [], + version: 1, + exceptions_list: [], + immutable: false, + related_integrations: [], + required_fields: [], + setup: '', + type: 'query', + language: 'kuery', + index: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'traces-apm*', + 'winlogbeat-*', + '-*elastic-cloud-logs-*', + ], + query: '*', + filters: [], + }, + ], + }, + { + field: 'signal.rule.version', + value: ['1'], + }, + { + field: 'signal.original_event.kind', + value: ['signal'], + }, + { + field: 'file.Ext.monotonic_id', + value: [157727], + }, + { + field: 'kibana.alert.status', + value: ['active'], + }, + { + field: 'kibana.alert.last_detected', + value: ['2023-04-25T09:03:15.535Z'], + }, + { + field: 'kibana.alert.original_event.dataset', + value: ['endpoint.events.file'], + }, + { + field: 'signal.depth', + value: [2], + }, + { + field: 'signal.rule.immutable', + value: ['false'], + }, + { + field: 'event.sequence', + value: [940441], + }, + { + field: 'kibana.alert.rule.rule_type_id', + value: ['siem.queryRule'], + }, + { + field: 'signal.rule.name', + value: ['Custom Rule'], + }, + { + field: 'signal.rule.rule_id', + value: ['caf0db0c-c7e2-455e-89b8-6b0d73619ae5'], + }, + { + field: 'event.module', + value: ['endpoint'], + }, + { + field: 'host.os.kernel', + value: ['1809 (10.0.17763.3406)'], + }, + { + field: 'kibana.alert.rule.license', + value: [''], + }, + { + field: 'kibana.alert.original_event.kind', + value: ['signal'], + }, + { + field: 'process.executable', + value: ['C:\\Program Files\\Winlogbeat\\winlogbeat.exe'], + }, + { + field: 'file.Ext.entropy', + value: [5.273971112252894], + }, + { + field: 'signal.rule.description', + value: ['Custom Rule'], + }, + { + field: 'kibana.alert.rule.updated_at', + value: ['2023-04-25T09:03:13.384Z'], + }, + { + field: 'data_stream.namespace', + value: ['default'], + }, + { + field: 'file.size', + value: [1408], + }, + { + field: 'message', + value: ['Endpoint file event'], + }, + { + field: 'process.Ext.code_signature.exists', + value: [true], + }, + { + field: 'kibana.alert.original_event.sequence', + value: [940441], + }, + { + field: 'signal.original_event.action', + value: ['exec'], + }, + { + field: 'kibana.alert.rule.created_at', + value: ['2023-04-25T09:03:13.384Z'], + }, + { + field: 'signal.rule.to', + value: ['now'], + }, + { + field: 'event.type', + value: ['creation'], + }, + { + field: 'process.entry_leader.entity_id', + value: ['some98'], + }, + { + field: 'kibana.space_ids', + value: ['default'], + }, + { + field: 'kibana.alert.rule.meta.from', + value: ['100000000m'], + }, + { + field: 'event.dataset', + value: ['endpoint.events.file'], + }, + { + field: 'kibana.alert.original_time', + value: ['2022-10-27T12:24:19.714Z'], + }, + { + field: '_id', + value: '1066d5c3675d7eef4b9e84af64e795be39d1896ec791049eb87a39eb9675a2aa', + }, + { + field: '_index', + value: '.internal.alerts-security.alerts-default-000001', + }, + ], +]; diff --git a/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_cell_actions.test.tsx b/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_cell_actions.test.tsx new file mode 100644 index 0000000000000..16228ec498358 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_cell_actions.test.tsx @@ -0,0 +1,148 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + createSecuritySolutionStorageMock, + mockGlobalState, + SUB_PLUGINS_REDUCER, + TestProviders, +} from '../../../common/mock'; +import { TableId } from '@kbn/securitysolution-data-table'; +import { renderHook } from '@testing-library/react-hooks'; +import { getUseCellActionsHook } from './use_cell_actions'; +import { columns as mockColumns, data as mockData } from './mock/data'; +import type { + EuiDataGridColumn, + EuiDataGridColumnCellAction, + EuiDataGridColumnCellActionProps, + EuiDataGridRefProps, +} from '@elastic/eui'; +import { EuiButtonEmpty } from '@elastic/eui'; +import { render, screen } from '@testing-library/react'; +import type { ComponentProps, JSXElementConstructor, PropsWithChildren } from 'react'; +import React from 'react'; +import { makeAction } from '../../../common/components/cell_actions/mocks'; +import { VIEW_SELECTION } from '../../../../common/constants'; +import { createStore } from '../../../common/store'; +import { createStartServicesMock } from '@kbn/timelines-plugin/public/mock'; +import { BehaviorSubject } from 'rxjs'; + +const useCellActions = getUseCellActionsHook(TableId.test); + +const mockDataGridRef: { + current: EuiDataGridRefProps; +} = { + current: { + closeCellPopover: jest.fn(), + setIsFullScreen: jest.fn(), + setFocusedCell: jest.fn(), + openCellPopover: jest.fn(), + }, +}; + +const renderCellAction = ( + columnCellAction: EuiDataGridColumnCellAction, + props: Partial = {} +) => { + const CellActions = columnCellAction as JSXElementConstructor; + return render( + + ); +}; + +const compatibleActions = [makeAction('action1')]; + +const withCustomPropsAndCellActions = (props: ComponentProps) => { + const TestProviderWithCustomProps = (_props: PropsWithChildren>) => ( + {_props.children} + ); + TestProviderWithCustomProps.displayName = 'TestProviderWithCustomProps'; + return TestProviderWithCustomProps; +}; + +const TestProviderWithActions = withCustomPropsAndCellActions({ cellActions: compatibleActions }); + +const mockedStateWithEventRenderedView: typeof mockGlobalState = { + ...mockGlobalState, + dataTable: { + ...mockGlobalState.dataTable, + tableById: { + ...mockGlobalState.dataTable.tableById, + [TableId.test]: { + ...mockGlobalState.dataTable.tableById[TableId.test], + viewMode: VIEW_SELECTION.eventRenderedView, + }, + }, + }, +}; +export const kibanaObservable = new BehaviorSubject(createStartServicesMock()); +const { storage } = createSecuritySolutionStorageMock(); + +const TestProviderWithCustomStateAndActions = withCustomPropsAndCellActions({ + cellActions: compatibleActions, + store: createStore( + mockedStateWithEventRenderedView, + SUB_PLUGINS_REDUCER, + kibanaObservable, + storage + ), +}); + +describe('getUseCellActionsHook', () => { + it('should render cell actions correctly for gridView view', async () => { + const { result, waitForNextUpdate } = renderHook( + () => + useCellActions({ + columns: mockColumns as unknown as EuiDataGridColumn[], + data: mockData, + dataGridRef: mockDataGridRef, + ecsData: [], + pageSize: 10, + }), + { + wrapper: TestProviderWithActions, + } + ); + + await waitForNextUpdate(); + + const cellAction = result.current.getCellActions('host.name', 0)[0]; + + renderCellAction(cellAction); + + expect(screen.getByTestId('dataGridColumnCellAction-action1')).toBeInTheDocument(); + }); + + it('should not render cell actions correctly for eventRendered view', async () => { + const { result, waitForNextUpdate } = renderHook( + () => + useCellActions({ + columns: mockColumns as unknown as EuiDataGridColumn[], + data: mockData, + dataGridRef: mockDataGridRef, + ecsData: [], + pageSize: 10, + }), + { + wrapper: TestProviderWithCustomStateAndActions, + } + ); + + const cellAction = result.current.getCellActions('host.name', 0); + + await waitForNextUpdate(); + + expect(cellAction).toHaveLength(0); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx index 713f18bb908ad..05954a5d0fbbc 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx @@ -6,7 +6,6 @@ */ import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import type { ActionExecutionContext } from '@kbn/ui-actions-plugin/public'; import type { Filter, Query } from '@kbn/es-query'; import { EuiFlexItem, EuiLoadingContent } from '@elastic/eui'; import React, { useCallback, useMemo } from 'react'; @@ -30,9 +29,9 @@ import { } from '../../../components/alerts_kpis/common/config'; import { AlertsCountPanel } from '../../../components/alerts_kpis/alerts_count_panel'; import { GROUP_BY_LABEL } from '../../../components/alerts_kpis/common/translations'; -import { RESET_GROUP_BY_FIELDS } from '../../../../common/components/chart_settings_popover/configurations/default/translations'; import { useQueryToggle } from '../../../../common/containers/query_toggle'; import type { AddFilterProps } from '../../../components/alerts_kpis/common/types'; +import { createResetGroupByFieldAction } from '../../../components/alerts_kpis/alerts_histogram_panel/helpers'; const TREND_CHART_HEIGHT = 280; // px const CHART_PANEL_HEIGHT = 375; // px @@ -124,33 +123,18 @@ const ChartPanelsComponent: React.FC = ({ onResetStackByField1(); }, [onResetStackByField0, onResetStackByField1]); - const resetGroupByFieldAction = useMemo( - () => [ - { - id: 'resetGroupByField', + const handleResetGroupByFieldAction = useCallback(() => { + onReset(); + updateCommonStackBy0(DEFAULT_STACK_BY_FIELD); - getDisplayName(context: ActionExecutionContext): string { - return RESET_GROUP_BY_FIELDS; - }, - getIconType(context: ActionExecutionContext): string | undefined { - return 'editorRedo'; - }, - type: 'actionButton', - async isCompatible(context: ActionExecutionContext): Promise { - return true; - }, - async execute(context: ActionExecutionContext): Promise { - onReset(); - updateCommonStackBy0(DEFAULT_STACK_BY_FIELD); + if (updateCommonStackBy1 != null) { + updateCommonStackBy1(DEFAULT_STACK_BY_FIELD1); + } + }, [onReset, updateCommonStackBy0, updateCommonStackBy1]); - if (updateCommonStackBy1 != null) { - updateCommonStackBy1(DEFAULT_STACK_BY_FIELD1); - } - }, - order: 5, - }, - ], - [onReset, updateCommonStackBy0, updateCommonStackBy1] + const resetGroupByFieldAction = useMemo( + () => [createResetGroupByFieldAction({ callback: handleResetGroupByFieldAction, order: 5 })], + [handleResetGroupByFieldAction] ); const chartOptionsContextMenu = useCallback( diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx index 13836e658eee7..c340c08ad7268 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx @@ -29,9 +29,10 @@ import { createFilterManagerMock } from '@kbn/data-plugin/public/query/filter_ma import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; import { createStubDataView } from '@kbn/data-views-plugin/common/data_view.stub'; import { useListsConfig } from '../../containers/detection_engine/lists/use_lists_config'; -import type { FilterGroupProps } from '../../../common/components/filter_group/types'; import { FilterGroup } from '../../../common/components/filter_group'; import type { AlertsTableComponentProps } from '../../components/alerts_table/alerts_grouping'; +import { getMockedFilterGroupWithCustomFilters } from '../../../common/components/filter_group/mocks'; +import { TableId } from '@kbn/securitysolution-data-table'; // Test will fail because we will to need to mock some core services to make the test work // For now let's forget about SiemSearchBar and QueryBar @@ -171,7 +172,46 @@ const state: State = { }; const { storage } = createSecuritySolutionStorageMock(); -const store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + +const getStoreWithCustomState = (newState: State = state) => { + return createStore(newState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); +}; + +const store = getStoreWithCustomState(); + +const stateWithBuildingBlockAlertsEnabled: State = { + ...state, + dataTable: { + ...state.dataTable, + tableById: { + ...state.dataTable.tableById, + [TableId.test]: { + ...state.dataTable.tableById[TableId.test], + additionalFilters: { + showOnlyThreatIndicatorAlerts: false, + showBuildingBlockAlerts: true, + }, + }, + }, + }, +}; + +const stateWithThreatIndicatorsAlertEnabled: State = { + ...state, + dataTable: { + ...state.dataTable, + tableById: { + ...state.dataTable.tableById, + [TableId.test]: { + ...state.dataTable.tableById[TableId.test], + additionalFilters: { + showOnlyThreatIndicatorAlerts: true, + showBuildingBlockAlerts: false, + }, + }, + }, + }, +}; jest.mock('../../components/alerts_table/timeline_actions/use_add_bulk_to_timeline', () => ({ useAddBulkToTimelineAction: jest.fn(() => {}), @@ -230,69 +270,136 @@ describe('DetectionEnginePageComponent', () => { }); }); - it('the pageFiltersUpdateHandler updates status when a multi status filter is passed', async () => { - (FilterGroup as jest.Mock).mockImplementationOnce(({ onFilterChange }: FilterGroupProps) => { - if (onFilterChange) { - // once with status - onFilterChange([ - { - meta: { - index: 'security-solution-default', - key: 'kibana.alert.workflow_status', - params: ['open', 'acknowledged'], - }, - }, - ]); - } - return ; - }); + it('should pass building block filter to the alert Page Controls', async () => { + const MockedFilterGroup = FilterGroup as jest.Mock; + MockedFilterGroup.mockImplementationOnce(getMockedFilterGroupWithCustomFilters()); await waitFor(() => { render( - + ); }); - // when statusFilter updates, we call mockStatusCapture in test mocks - expect(mockStatusCapture).toHaveBeenNthCalledWith(1, []); - expect(mockStatusCapture).toHaveBeenNthCalledWith(2, ['open', 'acknowledged']); - }); - it('the pageFiltersUpdateHandler updates status when a single status filter is passed', async () => { - (FilterGroup as jest.Mock).mockImplementationOnce(({ onFilterChange }: FilterGroupProps) => { - if (onFilterChange) { - // once with status - onFilterChange([ + expect(MockedFilterGroup).toHaveBeenCalledWith( + expect.objectContaining({ + filters: [ { meta: { - index: 'security-solution-default', - key: 'kibana.alert.workflow_status', + alias: null, + negate: true, disabled: false, + type: 'exists', + key: 'kibana.alert.building_block_type', + value: 'exists', }, query: { - match_phrase: { - 'kibana.alert.workflow_status': 'open', + exists: { + field: 'kibana.alert.building_block_type', }, }, }, + ], + }), + expect.anything() + ); + }); + + it('should pass threat Indicator filter to the alert Page Controls', async () => { + const MockedFilterGroup = FilterGroup as jest.Mock; + MockedFilterGroup.mockImplementationOnce(getMockedFilterGroupWithCustomFilters()); + + await waitFor(() => { + render( + + + + + + ); + }); + + expect(MockedFilterGroup).toHaveBeenCalledWith( + expect.objectContaining({ + filters: [ { meta: { - index: 'security-solution-default', - key: 'kibana.alert.severity', + alias: null, + negate: true, disabled: false, + type: 'exists', + key: 'kibana.alert.building_block_type', + value: 'exists', }, query: { - match_phrase: { - 'kibana.alert.severity': 'low', + exists: { + field: 'kibana.alert.building_block_type', }, }, }, - ]); - } - return ; + ], + }), + expect.anything() + ); + }); + + it('the pageFiltersUpdateHandler updates status when a multi status filter is passed', async () => { + (FilterGroup as jest.Mock).mockImplementationOnce( + getMockedFilterGroupWithCustomFilters([ + { + meta: { + index: 'security-solution-default', + key: 'kibana.alert.workflow_status', + params: ['open', 'acknowledged'], + }, + }, + ]) + ); + await waitFor(() => { + render( + + + + + + ); }); + // when statusFilter updates, we call mockStatusCapture in test mocks + expect(mockStatusCapture).toHaveBeenNthCalledWith(1, []); + expect(mockStatusCapture).toHaveBeenNthCalledWith(2, ['open', 'acknowledged']); + }); + + it('the pageFiltersUpdateHandler updates status when a single status filter is passed', async () => { + (FilterGroup as jest.Mock).mockImplementationOnce( + getMockedFilterGroupWithCustomFilters([ + { + meta: { + index: 'security-solution-default', + key: 'kibana.alert.workflow_status', + disabled: false, + }, + query: { + match_phrase: { + 'kibana.alert.workflow_status': 'open', + }, + }, + }, + { + meta: { + index: 'security-solution-default', + key: 'kibana.alert.severity', + disabled: false, + }, + query: { + match_phrase: { + 'kibana.alert.severity': 'low', + }, + }, + }, + ]) + ); await waitFor(() => { render( @@ -308,26 +415,22 @@ describe('DetectionEnginePageComponent', () => { }); it('the pageFiltersUpdateHandler clears status when no status filter is passed', async () => { - (FilterGroup as jest.Mock).mockImplementationOnce(({ onFilterChange }: FilterGroupProps) => { - if (onFilterChange) { - // once with status - onFilterChange([ - { - meta: { - index: 'security-solution-default', - key: 'kibana.alert.severity', - disabled: false, - }, - query: { - match_phrase: { - 'kibana.alert.severity': 'low', - }, + (FilterGroup as jest.Mock).mockImplementationOnce( + getMockedFilterGroupWithCustomFilters([ + { + meta: { + index: 'security-solution-default', + key: 'kibana.alert.severity', + disabled: false, + }, + query: { + match_phrase: { + 'kibana.alert.severity': 'low', }, }, - ]); - } - return ; - }); + }, + ]) + ); await waitFor(() => { render( diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx index 1ccfaea4584ee..27c9a27e130b4 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx @@ -77,13 +77,13 @@ import { useKibana } from '../../../common/lib/kibana'; import { NoPrivileges } from '../../../common/components/no_privileges'; import { HeaderPage } from '../../../common/components/header_page'; import { LandingPageComponent } from '../../../common/components/landing_page'; -import { DetectionPageFilterSet } from '../../components/detection_page_filters'; import type { FilterGroupHandler } from '../../../common/components/filter_group/types'; import type { Status } from '../../../../common/detection_engine/schemas/common/schemas'; import { AlertsTableFilterGroup } from '../../components/alerts_table/alerts_filter_group'; import { GroupedAlertsTable } from '../../components/alerts_table/alerts_grouping'; import { AlertsTableComponent } from '../../components/alerts_table'; import type { AddFilterProps } from '../../components/alerts_kpis/common/types'; +import { DetectionPageFilterSet } from '../../components/detection_page_filters'; /** * Need a 100% height here to account for the graph/analyze tool, which sets no explicit height parameters, but fills the available space. */ @@ -172,6 +172,14 @@ const DetectionEnginePageComponent: React.FC = ({ const { filterManager } = data.query; + const topLevelFilters = useMemo(() => { + return [ + ...filters, + ...buildShowBuildingBlockFilter(showBuildingBlockAlerts), + ...buildThreatMatchFilter(showOnlyThreatIndicatorAlerts), + ]; + }, [showBuildingBlockAlerts, showOnlyThreatIndicatorAlerts, filters]); + const alertPageFilters = useMemo(() => { if (arePageFiltersEnabled) { return detectionPageFilters; @@ -230,13 +238,8 @@ const DetectionEnginePageComponent: React.FC = ({ ); const alertsDefaultFilters = useMemo( - () => [ - ...filters, - ...buildShowBuildingBlockFilter(showBuildingBlockAlerts), - ...buildThreatMatchFilter(showOnlyThreatIndicatorAlerts), - ...(alertPageFilters ?? []), - ], - [filters, showBuildingBlockAlerts, showOnlyThreatIndicatorAlerts, alertPageFilters] + () => [...topLevelFilters, ...(alertPageFilters ?? [])], + [topLevelFilters, alertPageFilters] ); // AlertsTable manages global filters itself, so not including `filters` @@ -350,7 +353,7 @@ const DetectionEnginePageComponent: React.FC = ({ = ({ /> ), [ + topLevelFilters, arePageFiltersEnabled, dataViewId, statusFilter, - filters, onFilterGroupChangedCallback, pageFiltersUpdateHandler, showUpdating, diff --git a/x-pack/plugins/security_solution/public/index.ts b/x-pack/plugins/security_solution/public/index.ts index 1f6f121e04209..7ac596d087fca 100644 --- a/x-pack/plugins/security_solution/public/index.ts +++ b/x-pack/plugins/security_solution/public/index.ts @@ -7,10 +7,10 @@ import type { PluginInitializerContext } from '@kbn/core/public'; import { Plugin } from './plugin'; -import type { PluginSetup } from './types'; +import type { PluginSetup, PluginStart } from './types'; export type { TimelineModel } from './timelines/store/timeline/model'; export const plugin = (context: PluginInitializerContext): Plugin => new Plugin(context); -export type { PluginSetup }; +export type { PluginSetup, PluginStart }; export { Plugin }; diff --git a/x-pack/plugins/security_solution/public/landing_pages/pages/explore.tsx b/x-pack/plugins/security_solution/public/landing_pages/pages/explore.tsx index 17a0d7569b965..26dd3009e1d03 100644 --- a/x-pack/plugins/security_solution/public/landing_pages/pages/explore.tsx +++ b/x-pack/plugins/security_solution/public/landing_pages/pages/explore.tsx @@ -7,14 +7,14 @@ import React from 'react'; import { SecurityPageName } from '../../app/types'; import { HeaderPage } from '../../common/components/header_page'; -import { useAppRootNavLink } from '../../common/components/navigation/nav_links'; +import { useRootNavLink } from '../../common/links/nav_links'; import { SecuritySolutionPageWrapper } from '../../common/components/page_wrapper'; import { SpyRoute } from '../../common/utils/route/spy_routes'; import { LandingLinksImages } from '../components/landing_links_images'; import { EXPLORE_PAGE_TITLE } from './translations'; export const ExploreLandingPage = () => { - const exploreLinks = useAppRootNavLink(SecurityPageName.exploreLanding)?.links ?? []; + const exploreLinks = useRootNavLink(SecurityPageName.exploreLanding)?.links ?? []; return ( diff --git a/x-pack/plugins/security_solution/public/landing_pages/pages/manage.test.tsx b/x-pack/plugins/security_solution/public/landing_pages/pages/manage.test.tsx index 67eb06b60cca6..e900fad75546a 100644 --- a/x-pack/plugins/security_solution/public/landing_pages/pages/manage.test.tsx +++ b/x-pack/plugins/security_solution/public/landing_pages/pages/manage.test.tsx @@ -10,14 +10,14 @@ import React from 'react'; import { SecurityPageName } from '../../app/types'; import { TestProviders } from '../../common/mock'; import { ManagementCategories } from './manage'; -import type { NavLinkItem } from '../../common/components/navigation/types'; +import type { NavigationLink } from '../../common/links'; const RULES_ITEM_LABEL = 'elastic rules!'; const EXCEPTIONS_ITEM_LABEL = 'exceptional!'; const CATEGORY_1_LABEL = 'first tests category'; const CATEGORY_2_LABEL = 'second tests category'; -const defaultAppManageLink: NavLinkItem = { +const defaultAppManageLink: NavigationLink = { id: SecurityPageName.administration, title: 'admin', categories: [ @@ -47,8 +47,8 @@ const defaultAppManageLink: NavLinkItem = { }; const mockAppManageLink = jest.fn(() => defaultAppManageLink); -jest.mock('../../common/components/navigation/nav_links', () => ({ - useAppRootNavLink: () => mockAppManageLink(), +jest.mock('../../common/links/nav_links', () => ({ + useRootNavLink: () => mockAppManageLink(), })); describe('ManagementCategories', () => { diff --git a/x-pack/plugins/security_solution/public/landing_pages/pages/manage.tsx b/x-pack/plugins/security_solution/public/landing_pages/pages/manage.tsx index cb77921a0b673..37e2391801cac 100644 --- a/x-pack/plugins/security_solution/public/landing_pages/pages/manage.tsx +++ b/x-pack/plugins/security_solution/public/landing_pages/pages/manage.tsx @@ -10,8 +10,8 @@ import styled from 'styled-components'; import { SecurityPageName } from '../../app/types'; import { HeaderPage } from '../../common/components/header_page'; -import { useAppRootNavLink } from '../../common/components/navigation/nav_links'; -import type { NavLinkItem } from '../../common/components/navigation/types'; +import { useRootNavLink } from '../../common/links/nav_links'; +import type { NavigationLink } from '../../common/links'; import { SecuritySolutionPageWrapper } from '../../common/components/page_wrapper'; import { SpyRoute } from '../../common/utils/route/spy_routes'; import { LandingLinksIcons } from '../components/landing_links_icons'; @@ -30,14 +30,14 @@ const StyledEuiHorizontalRule = styled(EuiHorizontalRule)` margin-bottom: ${({ theme }) => theme.eui.euiSizeL}; `; -type ManagementCategories = Array<{ label: string; links: NavLinkItem[] }>; +type ManagementCategories = Array<{ label: string; links: NavigationLink[] }>; const useManagementCategories = (): ManagementCategories => { - const { links = [], categories = [] } = useAppRootNavLink(SecurityPageName.administration) ?? {}; + const { links = [], categories = [] } = useRootNavLink(SecurityPageName.administration) ?? {}; const manageLinksById = Object.fromEntries(links.map((link) => [link.id, link])); return categories.reduce((acc, { label, linkIds }) => { - const linksItem = linkIds.reduce((linksAcc, linkId) => { + const linksItem = linkIds.reduce((linksAcc, linkId) => { if (manageLinksById[linkId]) { linksAcc.push(manageLinksById[linkId]); } diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/isolate.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/isolate.cy.ts index 4259c3d0b708b..cc949b39404e9 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/isolate.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/isolate.cy.ts @@ -7,7 +7,7 @@ import { getEndpointListPath } from '../../../common/routing'; import { - checkEndpointListForOnlyIsolatedHosts, + checkEndpointIsIsolated, checkFlyoutEndpointIsolation, filterOutIsolatedHosts, interceptActionRequests, @@ -30,8 +30,10 @@ import { indexEndpointRuleAlerts } from '../../tasks/index_endpoint_rule_alerts' describe('Isolate command', () => { describe('from Manage', () => { - let endpointData: ReturnTypeFromChainable; - let isolatedEndpointData: ReturnTypeFromChainable; + let endpointData: ReturnTypeFromChainable | undefined; + let isolatedEndpointData: ReturnTypeFromChainable | undefined; + let isolatedEndpointHostnames: [string, string]; + let endpointHostnames: [string, string]; before(() => { indexEndpointHosts({ @@ -40,6 +42,10 @@ describe('Isolate command', () => { isolation: false, }).then((indexEndpoints) => { endpointData = indexEndpoints; + endpointHostnames = [ + endpointData.data.hosts[0].host.name, + endpointData.data.hosts[1].host.name, + ]; }); indexEndpointHosts({ @@ -48,65 +54,68 @@ describe('Isolate command', () => { isolation: true, }).then((indexEndpoints) => { isolatedEndpointData = indexEndpoints; + isolatedEndpointHostnames = [ + isolatedEndpointData.data.hosts[0].host.name, + isolatedEndpointData.data.hosts[1].host.name, + ]; }); }); after(() => { if (endpointData) { endpointData.cleanup(); - // @ts-expect-error ignore setting to undefined endpointData = undefined; } if (isolatedEndpointData) { isolatedEndpointData.cleanup(); - // @ts-expect-error ignore setting to undefined isolatedEndpointData = undefined; } }); + beforeEach(() => { login(); }); - // FLAKY: https://github.com/elastic/security-team/issues/6518 - it.skip('should allow filtering endpoint by Isolated status', () => { + + it('should allow filtering endpoint by Isolated status', () => { cy.visit(APP_PATH + getEndpointListPath({ name: 'endpointList' })); closeAllToasts(); filterOutIsolatedHosts(); - cy.contains('Showing 2 endpoints'); - checkEndpointListForOnlyIsolatedHosts(); + isolatedEndpointHostnames.forEach(checkEndpointIsIsolated); + endpointHostnames.forEach((hostname) => { + cy.contains(hostname).should('not.exist'); + }); }); }); describe('from Alerts', () => { - let endpointData: ReturnTypeFromChainable; - let alertData: ReturnTypeFromChainable; + let endpointData: ReturnTypeFromChainable | undefined; + let alertData: ReturnTypeFromChainable | undefined; let hostname: string; before(() => { - indexEndpointHosts({ withResponseActions: false, isolation: false }) - .then((indexEndpoints) => { + indexEndpointHosts({ withResponseActions: false, isolation: false }).then( + (indexEndpoints) => { endpointData = indexEndpoints; hostname = endpointData.data.hosts[0].host.name; - }) - .then(() => { + return indexEndpointRuleAlerts({ endpointAgentId: endpointData.data.hosts[0].agent.id, endpointHostname: endpointData.data.hosts[0].host.name, endpointIsolated: false, }); - }); + } + ); }); after(() => { if (endpointData) { endpointData.cleanup(); - // @ts-expect-error ignore setting to undefined endpointData = undefined; } if (alertData) { alertData.cleanup(); - // @ts-expect-error ignore setting to undefined alertData = undefined; } }); @@ -181,9 +190,9 @@ describe('Isolate command', () => { }); describe('from Cases', () => { - let endpointData: ReturnTypeFromChainable; - let caseData: ReturnTypeFromChainable; - let alertData: ReturnTypeFromChainable; + let endpointData: ReturnTypeFromChainable | undefined; + let caseData: ReturnTypeFromChainable | undefined; + let alertData: ReturnTypeFromChainable | undefined; let caseAlertActions: ReturnType; let alertId: string; let caseUrlPath: string; @@ -199,41 +208,39 @@ describe('Isolate command', () => { .then((indexEndpoints) => { endpointData = indexEndpoints; hostname = endpointData.data.hosts[0].host.name; - }) - .then(() => { + return indexEndpointRuleAlerts({ endpointAgentId: endpointData.data.hosts[0].agent.id, endpointHostname: endpointData.data.hosts[0].host.name, endpointIsolated: false, - }).then((indexedAlert) => { - alertData = indexedAlert; - alertId = alertData.alerts[0]._id; }); }) - .then(() => { - caseAlertActions = addAlertsToCase({ - caseId: caseData.data.id, - alertIds: [alertId], - }); + .then((indexedAlert) => { + alertData = indexedAlert; + alertId = alertData.alerts[0]._id; + + if (caseData) { + caseAlertActions = addAlertsToCase({ + caseId: caseData.data.id, + alertIds: [alertId], + }); + } }); }); after(() => { if (caseData) { caseData.cleanup(); - // @ts-expect-error ignore setting to undefined caseData = undefined; } if (endpointData) { endpointData.cleanup(); - // @ts-expect-error ignore setting to undefined endpointData = undefined; } if (alertData) { alertData.cleanup(); - // @ts-expect-error ignore setting to undefined alertData = undefined; } }); diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/policy_response.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/policy_response.cy.ts index 3100d4a64fec7..63d9f26514ba5 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/policy_response.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/policy_response.cy.ts @@ -15,7 +15,8 @@ import { navigateToFleetAgentDetails } from '../../screens/fleet'; import { EndpointPolicyResponseGenerator } from '../../../../../common/endpoint/data_generators/endpoint_policy_response_generator'; import { descriptions } from '../../../components/policy_response/policy_response_friendly_names'; -describe('Endpoint Policy Response', () => { +// FLAKY: https://github.com/elastic/security-team/issues/6518 +describe.skip('Endpoint Policy Response', () => { let loadedEndpoint: CyIndexEndpointHosts; let endpointMetadata: HostMetadata; let loadedPolicyResponse: IndexedEndpointPolicyResponse; diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/response_console.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/response_console.cy.ts new file mode 100644 index 0000000000000..2877a91f6b63c --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/mocked_data/response_console.cy.ts @@ -0,0 +1,230 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ActionDetails } from '../../../../../common/endpoint/types'; +import type { ReturnTypeFromChainable } from '../../types'; +import { indexEndpointHosts } from '../../tasks/index_endpoint_hosts'; +import { + checkReturnedProcessesTable, + inputConsoleCommand, + openResponseConsoleFromEndpointList, + performCommandInputChecks, + submitCommand, + waitForEndpointListPageToBeLoaded, +} from '../../tasks/response_console'; +import { + checkEndpointIsIsolated, + checkEndpointIsNotIsolated, + interceptActionRequests, + sendActionResponse, +} from '../../tasks/isolate'; +import { login } from '../../tasks/login'; + +describe('Response console', () => { + beforeEach(() => { + login(); + }); + + describe('Isolate command', () => { + let endpointData: ReturnTypeFromChainable; + let endpointHostname: string; + let isolateRequestResponse: ActionDetails; + + before(() => { + indexEndpointHosts({ withResponseActions: false, isolation: false }).then( + (indexEndpoints) => { + endpointData = indexEndpoints; + endpointHostname = endpointData.data.hosts[0].host.name; + } + ); + }); + + after(() => { + if (endpointData) { + endpointData.cleanup(); + // @ts-expect-error ignore setting to undefined + endpointData = undefined; + } + }); + + it('should isolate host from response console', () => { + waitForEndpointListPageToBeLoaded(endpointHostname); + checkEndpointIsNotIsolated(endpointHostname); + openResponseConsoleFromEndpointList(); + performCommandInputChecks('isolate'); + interceptActionRequests((responseBody) => { + isolateRequestResponse = responseBody; + }, 'isolate'); + + submitCommand(); + cy.contains('Action pending.').should('exist'); + cy.wait('@isolate').then(() => { + sendActionResponse(isolateRequestResponse); + }); + cy.contains('Action completed.', { timeout: 120000 }).should('exist'); + waitForEndpointListPageToBeLoaded(endpointHostname); + checkEndpointIsIsolated(endpointHostname); + }); + }); + + describe('Release command', () => { + let endpointData: ReturnTypeFromChainable; + let endpointHostname: string; + let releaseRequestResponse: ActionDetails; + + before(() => { + indexEndpointHosts({ withResponseActions: false, isolation: true }).then((indexEndpoints) => { + endpointData = indexEndpoints; + endpointHostname = endpointData.data.hosts[0].host.name; + }); + }); + + after(() => { + if (endpointData) { + endpointData.cleanup(); + // @ts-expect-error ignore setting to undefined + endpointData = undefined; + } + }); + + it('should release host from response console', () => { + waitForEndpointListPageToBeLoaded(endpointHostname); + checkEndpointIsIsolated(endpointHostname); + openResponseConsoleFromEndpointList(); + performCommandInputChecks('release'); + interceptActionRequests((responseBody) => { + releaseRequestResponse = responseBody; + }, 'release'); + submitCommand(); + cy.contains('Action pending.').should('exist'); + cy.wait('@release').then(() => { + sendActionResponse(releaseRequestResponse); + }); + cy.contains('Action completed.', { timeout: 120000 }).should('exist'); + waitForEndpointListPageToBeLoaded(endpointHostname); + checkEndpointIsNotIsolated(endpointHostname); + }); + }); + + describe('Processes command', () => { + let endpointData: ReturnTypeFromChainable; + let endpointHostname: string; + let processesRequestResponse: ActionDetails; + + before(() => { + indexEndpointHosts({ withResponseActions: false, isolation: false }).then( + (indexEndpoints) => { + endpointData = indexEndpoints; + endpointHostname = endpointData.data.hosts[0].host.name; + } + ); + }); + + after(() => { + if (endpointData) { + endpointData.cleanup(); + // @ts-expect-error ignore setting to undefined + endpointData = undefined; + } + }); + + it('should return processes from response console', () => { + waitForEndpointListPageToBeLoaded(endpointHostname); + openResponseConsoleFromEndpointList(); + performCommandInputChecks('processes'); + interceptActionRequests((responseBody) => { + processesRequestResponse = responseBody; + }, 'processes'); + submitCommand(); + cy.contains('Action pending.').should('exist'); + cy.wait('@processes').then(() => { + sendActionResponse(processesRequestResponse); + }); + cy.getByTestSubj('getProcessesSuccessCallout', { timeout: 120000 }).within(() => { + checkReturnedProcessesTable(); + }); + }); + }); + + describe('Kill process command', () => { + let endpointData: ReturnTypeFromChainable; + let endpointHostname: string; + let killProcessRequestResponse: ActionDetails; + + before(() => { + indexEndpointHosts({ withResponseActions: false, isolation: false }).then( + (indexEndpoints) => { + endpointData = indexEndpoints; + endpointHostname = endpointData.data.hosts[0].host.name; + } + ); + }); + + after(() => { + if (endpointData) { + endpointData.cleanup(); + // @ts-expect-error ignore setting to undefined + endpointData = undefined; + } + }); + + it('should kill process from response console', () => { + waitForEndpointListPageToBeLoaded(endpointHostname); + openResponseConsoleFromEndpointList(); + inputConsoleCommand(`kill-process --pid 1`); + + interceptActionRequests((responseBody) => { + killProcessRequestResponse = responseBody; + }, 'kill-process'); + submitCommand(); + cy.contains('Action pending.').should('exist'); + cy.wait('@kill-process').then(() => { + sendActionResponse(killProcessRequestResponse); + }); + cy.contains('Action completed.', { timeout: 120000 }).should('exist'); + }); + }); + + describe('Suspend process command', () => { + let endpointData: ReturnTypeFromChainable; + let endpointHostname: string; + let suspendProcessRequestResponse: ActionDetails; + + before(() => { + indexEndpointHosts({ withResponseActions: false, isolation: false }).then( + (indexEndpoints) => { + endpointData = indexEndpoints; + endpointHostname = endpointData.data.hosts[0].host.name; + } + ); + }); + + after(() => { + if (endpointData) { + endpointData.cleanup(); + // @ts-expect-error ignore setting to undefined + endpointData = undefined; + } + }); + + it('should suspend process from response console', () => { + waitForEndpointListPageToBeLoaded(endpointHostname); + openResponseConsoleFromEndpointList(); + inputConsoleCommand(`suspend-process --pid 1`); + + interceptActionRequests((responseBody) => { + suspendProcessRequestResponse = responseBody; + }, 'suspend-process'); + submitCommand(); + cy.contains('Action pending.').should('exist'); + cy.wait('@suspend-process').then(() => { + sendActionResponse(suspendProcessRequestResponse); + }); + cy.contains('Action completed.', { timeout: 120000 }).should('exist'); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/cypress/support/data_loaders.ts b/x-pack/plugins/security_solution/public/management/cypress/support/data_loaders.ts index 4a006fdacc353..bd0438542dc73 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/support/data_loaders.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/support/data_loaders.ts @@ -8,7 +8,10 @@ // / import type { CasePostRequest } from '@kbn/cases-plugin/common/api'; -import { sendEndpointActionResponse } from '../../../../scripts/endpoint/agent_emulator/services/endpoint_response_actions'; +import { + sendEndpointActionResponse, + sendFleetActionResponse, +} from '../../../../scripts/endpoint/common/response_actions'; import type { DeleteAllEndpointDataResponse } from '../../../../scripts/endpoint/common/delete_all_endpoint_data'; import { deleteAllEndpointData } from '../../../../scripts/endpoint/common/delete_all_endpoint_data'; import { waitForEndpointToStreamData } from '../../../../scripts/endpoint/common/endpoint_metadata_services'; @@ -21,11 +24,7 @@ import { deleteIndexedEndpointPolicyResponse, indexEndpointPolicyResponse, } from '../../../../common/endpoint/data_loaders/index_endpoint_policy_response'; -import type { - ActionDetails, - HostPolicyResponse, - LogsEndpointActionResponse, -} from '../../../../common/endpoint/types'; +import type { ActionDetails, HostPolicyResponse } from '../../../../common/endpoint/types'; import type { IndexEndpointHostsCyTaskOptions } from '../types'; import type { IndexedEndpointRuleAlerts, @@ -162,9 +161,17 @@ export const dataLoaders = ( sendHostActionResponse: async (data: { action: ActionDetails; state: { state?: 'success' | 'failure' }; - }): Promise => { + }): Promise => { const { esClient } = await stackServicesPromise; - return sendEndpointActionResponse(esClient, data.action, { state: data.state.state }); + const fleetResponse = await sendFleetActionResponse(esClient, data.action, { + state: data.state.state, + }); + + if (!fleetResponse.error) { + await sendEndpointActionResponse(esClient, data.action, { state: data.state.state }); + } + + return null; }, deleteAllEndpointData: async ({ diff --git a/x-pack/plugins/security_solution/public/management/cypress/tasks/isolate.ts b/x-pack/plugins/security_solution/public/management/cypress/tasks/isolate.ts index 8a05d790e19bd..cf9ae47b1209d 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/tasks/isolate.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/tasks/isolate.ts @@ -143,3 +143,18 @@ export const checkEndpointListForOnlyUnIsolatedHosts = (): void => checkEndpointListForIsolatedHosts(false); export const checkEndpointListForOnlyIsolatedHosts = (): void => checkEndpointListForIsolatedHosts(true); + +export const checkEndpointIsolationStatus = ( + endpointHostname: string, + expectIsolated: boolean +): void => { + const chainer = expectIsolated ? 'contain.text' : 'not.contain.text'; + + cy.contains(endpointHostname).parents('td').siblings('td').eq(0).should(chainer, 'Isolated'); +}; + +export const checkEndpointIsIsolated = (endpointHostname: string): void => + checkEndpointIsolationStatus(endpointHostname, true); + +export const checkEndpointIsNotIsolated = (endpointHostname: string): void => + checkEndpointIsolationStatus(endpointHostname, false); diff --git a/x-pack/plugins/security_solution/public/management/cypress/tasks/response_console.ts b/x-pack/plugins/security_solution/public/management/cypress/tasks/response_console.ts index 5932bc6abf3ec..9e5d2adc7d6e8 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/tasks/response_console.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/tasks/response_console.ts @@ -7,6 +7,7 @@ import { closeAllToasts } from './close_all_toasts'; import { APP_ENDPOINTS_PATH } from '../../../../common/constants'; +import Chainable = Cypress.Chainable; export const waitForEndpointListPageToBeLoaded = (endpointHostname: string): void => { cy.visit(APP_ENDPOINTS_PATH); @@ -56,3 +57,16 @@ export const performCommandInputChecks = (command: string) => { selectCommandFromHelpMenu(command); checkInputForCommandPresence(command); }; + +export const checkReturnedProcessesTable = (): Chainable> => { + ['USER', 'PID', 'ENTITY ID', 'COMMAND'].forEach((header) => { + cy.contains(header); + }); + + return cy + .get('tbody') + .find('tr') + .then((rows) => { + expect(rows.length).to.be.greaterThan(0); + }); +}; diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index 61ec29d956414..14b886920613b 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import type { Subscription } from 'rxjs'; -import { Subject } from 'rxjs'; +import { BehaviorSubject, Subject } from 'rxjs'; import { combineLatestWith } from 'rxjs/operators'; import type * as H from 'history'; import type { @@ -45,6 +45,7 @@ import { import { getDeepLinks, registerDeepLinksUpdater } from './app/deep_links'; import type { LinksPermissions } from './common/links'; import { updateAppLinks } from './common/links'; +import { navLinks$ } from './common/links/nav_links'; import { licenseService } from './common/hooks/use_license'; import type { SecuritySolutionUiConfigType } from './common/types'; import { ExperimentalFeaturesService } from './common/experimental_features_service'; @@ -86,6 +87,7 @@ export class Plugin implements IPlugin; constructor(private readonly initializerContext: PluginInitializerContext) { this.config = this.initializerContext.config.get(); @@ -93,7 +95,7 @@ export class Plugin implements IPlugin(true); this.telemetry = new TelemetryService(); } private appUpdater$ = new Subject(); @@ -168,6 +170,7 @@ export class Plugin implements IPlugin SecuritySolutionTemplateWrapper, }, savedObjectsManagement: startPluginsDeps.savedObjectsManagement, + isSidebarEnabled$: this.isSidebarEnabled$, telemetry: this.telemetry.start(), }; return services; @@ -245,7 +248,7 @@ export class Plugin implements IPlugin + this.isSidebarEnabled$.next(isSidebarEnabled), + }; } public stop() { diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index e6e10a9c5b3dd..ba5bd460ba376 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -5,6 +5,8 @@ * 2.0. */ +import type { BehaviorSubject, Observable } from 'rxjs'; + import type { AppLeaveHandler, CoreStart } from '@kbn/core/public'; import type { HomePublicPluginSetup } from '@kbn/home-plugin/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; @@ -46,6 +48,7 @@ import type { CloudExperimentsPluginStart } from '@kbn/cloud-experiments-plugin/ import type { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public'; import type { DataViewsServicePublic } from '@kbn/data-views-plugin/public'; import type { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; + import type { ResolverPluginSetup } from './resolver/types'; import type { Inspect } from '../common/search_strategy'; import type { Detections } from './detections'; @@ -62,6 +65,8 @@ import type { CloudDefend } from './cloud_defend'; import type { ThreatIntelligence } from './threat_intelligence'; import type { SecuritySolutionTemplateWrapper } from './app/home/template_wrapper'; import type { Explore } from './explore'; +import type { NavigationLink } from './common/links'; + import type { TelemetryClientStart } from './common/lib/telemetry'; import type { Dashboards } from './dashboards'; @@ -127,14 +132,18 @@ export type StartServices = CoreStart & getPluginWrapper: () => typeof SecuritySolutionTemplateWrapper; }; savedObjectsManagement: SavedObjectsManagementPluginStart; + isSidebarEnabled$: BehaviorSubject; telemetry: TelemetryClientStart; }; export interface PluginSetup { resolver: () => Promise; } -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface PluginStart {} + +export interface PluginStart { + navLinks$: Observable; + setIsSidebarEnabled: (isSidebarEnabled: boolean) => void; +} export interface AppObservableLibs { kibana: CoreStart; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/agent_emulator/services/action_responder.ts b/x-pack/plugins/security_solution/scripts/endpoint/agent_emulator/services/action_responder.ts index 8b91273a7cae7..8686698dfdfc6 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/agent_emulator/services/action_responder.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/agent_emulator/services/action_responder.ts @@ -9,12 +9,9 @@ import { set } from '@kbn/safer-lodash-set'; import type { Client } from '@elastic/elasticsearch'; import type { ToolingLog } from '@kbn/tooling-log'; import type { KbnClient } from '@kbn/test'; +import { sendEndpointActionResponse, sendFleetActionResponse } from '../../common/response_actions'; import { BaseRunningService } from '../../common/base_running_service'; -import { - fetchEndpointActionList, - sendEndpointActionResponse, - sendFleetActionResponse, -} from './endpoint_response_actions'; +import { fetchEndpointActionList } from './endpoint_response_actions'; import type { ActionDetails } from '../../../../common/endpoint/types'; /** diff --git a/x-pack/plugins/security_solution/scripts/endpoint/agent_emulator/services/endpoint_response_actions.ts b/x-pack/plugins/security_solution/scripts/endpoint/agent_emulator/services/endpoint_response_actions.ts index 25c2e5f6327be..03443882ad369 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/agent_emulator/services/endpoint_response_actions.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/agent_emulator/services/endpoint_response_actions.ts @@ -6,43 +6,9 @@ */ import type { KbnClient } from '@kbn/test'; -import type { Client } from '@elastic/elasticsearch'; -import { AGENT_ACTIONS_RESULTS_INDEX } from '@kbn/fleet-plugin/common'; -import * as cborx from 'cbor-x'; -import { basename } from 'path'; -import { generateFileMetadataDocumentMock } from '../../../../server/endpoint/services/actions/mocks'; -import { getFileDownloadId } from '../../../../common/endpoint/service/response_actions/get_file_download_id'; -import { checkInFleetAgent } from '../../common/fleet_services'; -import { sendEndpointMetadataUpdate } from '../../common/endpoint_metadata_services'; -import { FleetActionGenerator } from '../../../../common/endpoint/data_generators/fleet_action_generator'; -import { - ENDPOINT_ACTION_RESPONSES_INDEX, - BASE_ENDPOINT_ACTION_ROUTE, - FILE_STORAGE_DATA_INDEX, - FILE_STORAGE_METADATA_INDEX, -} from '../../../../common/endpoint/constants'; -import type { - ActionDetails, - ActionListApiResponse, - EndpointActionData, - EndpointActionResponse, - LogsEndpointActionResponse, - GetProcessesActionOutputContent, - ResponseActionGetFileOutputContent, - ResponseActionGetFileParameters, - FileUploadMetadata, - ResponseActionExecuteOutputContent, -} from '../../../../common/endpoint/types'; +import { BASE_ENDPOINT_ACTION_ROUTE } from '../../../../common/endpoint/constants'; +import type { ActionListApiResponse } from '../../../../common/endpoint/types'; import type { EndpointActionListRequestQuery } from '../../../../common/endpoint/schema/actions'; -import { EndpointActionGenerator } from '../../../../common/endpoint/data_generators/endpoint_action_generator'; - -const ES_INDEX_OPTIONS = { headers: { 'X-elastic-product-origin': 'fleet' } }; - -export const fleetActionGenerator = new FleetActionGenerator(); - -export const endpointActionGenerator = new EndpointActionGenerator(); - -export const sleep = (ms: number = 1000) => new Promise((r) => setTimeout(r, ms)); export const fetchEndpointActionList = async ( kbn: KbnClient, @@ -76,279 +42,3 @@ export const fetchEndpointActionList = async ( throw error; } }; - -export const sendFleetActionResponse = async ( - esClient: Client, - action: ActionDetails, - { state }: { state?: 'success' | 'failure' } = {} -): Promise => { - const fleetResponse = fleetActionGenerator.generateResponse({ - action_id: action.id, - agent_id: action.agents[0], - action_response: { - endpoint: { - ack: true, - }, - }, - }); - - // 20% of the time we generate an error - if (state === 'failure' || (!state && fleetActionGenerator.randomFloat() < 0.2)) { - fleetResponse.action_response = {}; - fleetResponse.error = 'Agent failed to deliver message to endpoint due to unknown error'; - } else { - // show it as success (generator currently always generates a `error`, so delete it) - delete fleetResponse.error; - } - - await esClient.index( - { - index: AGENT_ACTIONS_RESULTS_INDEX, - body: fleetResponse, - refresh: 'wait_for', - }, - ES_INDEX_OPTIONS - ); - - return fleetResponse; -}; - -export const sendEndpointActionResponse = async ( - esClient: Client, - action: ActionDetails, - { state }: { state?: 'success' | 'failure' } = {} -): Promise => { - const endpointResponse = endpointActionGenerator.generateResponse({ - agent: { id: action.agents[0] }, - EndpointActions: { - action_id: action.id, - data: { - command: action.command as EndpointActionData['command'], - comment: '', - ...getOutputDataIfNeeded(action), - }, - started_at: action.startedAt, - }, - }); - - // 20% of the time we generate an error - if (state === 'failure' || (state !== 'success' && endpointActionGenerator.randomFloat() < 0.2)) { - endpointResponse.error = { - message: 'Endpoint encountered an error and was unable to apply action to host', - }; - - if ( - endpointResponse.EndpointActions.data.command === 'get-file' && - endpointResponse.EndpointActions.data.output - ) { - ( - endpointResponse.EndpointActions.data.output.content as ResponseActionGetFileOutputContent - ).code = endpointActionGenerator.randomGetFileFailureCode(); - } - - if ( - endpointResponse.EndpointActions.data.command === 'execute' && - endpointResponse.EndpointActions.data.output - ) { - ( - endpointResponse.EndpointActions.data.output.content as ResponseActionExecuteOutputContent - ).stderr = 'execute command timed out'; - } - } - - await esClient.index({ - index: ENDPOINT_ACTION_RESPONSES_INDEX, - body: endpointResponse, - refresh: 'wait_for', - }); - - // ------------------------------------------ - // Post Action Response tasks - // ------------------------------------------ - - // For isolate, If the response is not an error, then also send a metadata update - if (action.command === 'isolate' && !endpointResponse.error) { - for (const agentId of action.agents) { - await Promise.all([ - sendEndpointMetadataUpdate(esClient, agentId, { - Endpoint: { - state: { - isolation: true, - }, - }, - }), - - checkInFleetAgent(esClient, agentId), - ]); - } - } - - // For UnIsolate, if response is not an Error, then also send metadata update - if (action.command === 'unisolate' && !endpointResponse.error) { - for (const agentId of action.agents) { - await Promise.all([ - sendEndpointMetadataUpdate(esClient, agentId, { - Endpoint: { - state: { - isolation: false, - }, - }, - }), - - checkInFleetAgent(esClient, agentId), - ]); - } - } - - // For `get-file`, upload a file to ES - if ((action.command === 'execute' || action.command === 'get-file') && !endpointResponse.error) { - const filePath = - action.command === 'execute' - ? '/execute/file/path' - : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - ( - action as ActionDetails< - ResponseActionGetFileOutputContent, - ResponseActionGetFileParameters - > - )?.parameters?.path!; - - const fileName = basename(filePath.replace(/\\/g, '/')); - const fileMetaDoc: FileUploadMetadata = generateFileMetadataDocumentMock({ - action_id: action.id, - agent_id: action.agents[0], - contents: [ - { - sha256: '8d61673c9d782297b3c774ded4e3d88f31a8869a8f25cf5cdd402ba6822d1d28', - file_name: fileName ?? 'bad_file.txt', - path: filePath, - size: 4, - type: 'file', - }, - ], - file: { - attributes: ['archive', 'compressed'], - ChunkSize: 4194304, - compression: 'deflate', - hash: { - sha256: '8d61673c9d782297b3c774ded4e3d88f31a8869a8f25cf5cdd402ba6822d1d28', - }, - mime_type: 'application/zip', - name: action.command === 'execute' ? 'full-output.zip' : 'upload.zip', - extension: 'zip', - size: 125, - Status: 'READY', - type: 'file', - }, - src: 'endpoint', - }); - - // Index the file's metadata - const fileMeta = await esClient.index({ - index: FILE_STORAGE_METADATA_INDEX, - id: getFileDownloadId(action, action.agents[0]), - body: fileMetaDoc, - refresh: 'wait_for', - }); - - // Index the file content (just one chunk) - // call to `.index()` copied from File plugin here: - // https://github.com/elastic/kibana/blob/main/src/plugins/files/server/blob_storage_service/adapters/es/content_stream/content_stream.ts#L195 - await esClient - .index( - { - index: FILE_STORAGE_DATA_INDEX, - id: `${fileMeta._id}.0`, - document: cborx.encode({ - bid: fileMeta._id, - last: true, - data: Buffer.from( - 'UEsDBAoACQAAAFZeRFWpAsDLHwAAABMAAAAMABwAYmFkX2ZpbGUudHh0VVQJAANTVjxjU1Y8Y3V4CwABBPUBAAAEFAAAAMOcoyEq/Q4VyG02U9O0LRbGlwP/y5SOCfRKqLz1rsBQSwcIqQLAyx8AAAATAAAAUEsBAh4DCgAJAAAAVl5EVakCwMsfAAAAEwAAAAwAGAAAAAAAAQAAAKSBAAAAAGJhZF9maWxlLnR4dFVUBQADU1Y8Y3V4CwABBPUBAAAEFAAAAFBLBQYAAAAAAQABAFIAAAB1AAAAAAA=', - 'base64' - ), - }), - refresh: 'wait_for', - }, - { - headers: { - 'content-type': 'application/cbor', - accept: 'application/json', - }, - } - ) - .then(() => sleep(2000)); - } - - return endpointResponse; -}; - -type ResponseOutput = Pick< - LogsEndpointActionResponse['EndpointActions']['data'], - 'output' ->; - -const getOutputDataIfNeeded = (action: ActionDetails): ResponseOutput => { - const commentUppercase = (action?.comment ?? '').toUpperCase(); - - switch (action.command) { - case 'running-processes': - return { - output: { - type: 'json', - content: { - entries: endpointActionGenerator.randomResponseActionProcesses(100), - }, - }, - } as ResponseOutput; - - case 'get-file': - return { - output: { - type: 'json', - content: { - code: 'ra_get-file_success_done', - zip_size: 123, - contents: [ - { - type: 'file', - path: ( - action as ActionDetails< - ResponseActionGetFileOutputContent, - ResponseActionGetFileParameters - > - ).parameters?.path, - size: 1234, - file_name: 'bad_file.txt', - sha256: '9558c5cb39622e9b3653203e772b129d6c634e7dbd7af1b244352fc1d704601f', - }, - ], - }, - }, - } as ResponseOutput; - - case 'execute': - const executeOutput: Partial = { - output_file_id: getFileDownloadId(action, action.agents[0]), - }; - - // Error? - if (commentUppercase.indexOf('EXECUTE:FAILURE') > -1) { - executeOutput.stdout = ''; - executeOutput.stdout_truncated = false; - executeOutput.output_file_stdout_truncated = false; - } else { - executeOutput.stderr = ''; - executeOutput.stderr_truncated = false; - executeOutput.output_file_stderr_truncated = false; - } - - return { - output: endpointActionGenerator.generateExecuteActionResponseOutput({ - content: executeOutput, - }), - } as ResponseOutput; - - default: - return { output: undefined }; - } -}; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/response_actions.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/response_actions.ts new file mode 100644 index 0000000000000..cac110ce0cde1 --- /dev/null +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/response_actions.ts @@ -0,0 +1,311 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { Client } from '@elastic/elasticsearch'; +import { basename } from 'path'; +import * as cborx from 'cbor-x'; +import { AGENT_ACTIONS_RESULTS_INDEX } from '@kbn/fleet-plugin/common'; +import { FleetActionGenerator } from '../../../common/endpoint/data_generators/fleet_action_generator'; +import { EndpointActionGenerator } from '../../../common/endpoint/data_generators/endpoint_action_generator'; +import type { + ActionDetails, + EndpointActionData, + EndpointActionResponse, + FileUploadMetadata, + GetProcessesActionOutputContent, + LogsEndpointActionResponse, + ResponseActionExecuteOutputContent, + ResponseActionGetFileOutputContent, + ResponseActionGetFileParameters, +} from '../../../common/endpoint/types'; +import { getFileDownloadId } from '../../../common/endpoint/service/response_actions/get_file_download_id'; +import { + ENDPOINT_ACTION_RESPONSES_INDEX, + FILE_STORAGE_DATA_INDEX, + FILE_STORAGE_METADATA_INDEX, +} from '../../../common/endpoint/constants'; +import { sendEndpointMetadataUpdate } from './endpoint_metadata_services'; +import { checkInFleetAgent } from './fleet_services'; +import { generateFileMetadataDocumentMock } from '../../../server/endpoint/services/actions/mocks'; + +const ES_INDEX_OPTIONS = { headers: { 'X-elastic-product-origin': 'fleet' } }; +export const fleetActionGenerator = new FleetActionGenerator(); +export const endpointActionGenerator = new EndpointActionGenerator(); +export const sleep = (ms: number = 1000) => new Promise((r) => setTimeout(r, ms)); + +export const sendFleetActionResponse = async ( + esClient: Client, + action: ActionDetails, + { state }: { state?: 'success' | 'failure' } = {} +): Promise => { + const fleetResponse = fleetActionGenerator.generateResponse({ + action_id: action.id, + agent_id: action.agents[0], + action_response: { + endpoint: { + ack: true, + }, + }, + }); + + // 20% of the time we generate an error + if (state === 'failure' || (!state && fleetActionGenerator.randomFloat() < 0.2)) { + fleetResponse.action_response = {}; + fleetResponse.error = 'Agent failed to deliver message to endpoint due to unknown error'; + } else { + // show it as success (generator currently always generates a `error`, so delete it) + delete fleetResponse.error; + } + + await esClient.index( + { + index: AGENT_ACTIONS_RESULTS_INDEX, + body: fleetResponse, + refresh: 'wait_for', + }, + ES_INDEX_OPTIONS + ); + + return fleetResponse; +}; +export const sendEndpointActionResponse = async ( + esClient: Client, + action: ActionDetails, + { state }: { state?: 'success' | 'failure' } = {} +): Promise => { + const endpointResponse = endpointActionGenerator.generateResponse({ + agent: { id: action.agents[0] }, + EndpointActions: { + action_id: action.id, + data: { + command: action.command as EndpointActionData['command'], + comment: '', + ...getOutputDataIfNeeded(action), + }, + started_at: action.startedAt, + }, + }); + + // 20% of the time we generate an error + if (state === 'failure' || (state !== 'success' && endpointActionGenerator.randomFloat() < 0.2)) { + endpointResponse.error = { + message: 'Endpoint encountered an error and was unable to apply action to host', + }; + + if ( + endpointResponse.EndpointActions.data.command === 'get-file' && + endpointResponse.EndpointActions.data.output + ) { + ( + endpointResponse.EndpointActions.data.output.content as ResponseActionGetFileOutputContent + ).code = endpointActionGenerator.randomGetFileFailureCode(); + } + + if ( + endpointResponse.EndpointActions.data.command === 'execute' && + endpointResponse.EndpointActions.data.output + ) { + ( + endpointResponse.EndpointActions.data.output.content as ResponseActionExecuteOutputContent + ).stderr = 'execute command timed out'; + } + } + + await esClient.index({ + index: ENDPOINT_ACTION_RESPONSES_INDEX, + body: endpointResponse, + refresh: 'wait_for', + }); + + // ------------------------------------------ + // Post Action Response tasks + // ------------------------------------------ + + // For isolate, If the response is not an error, then also send a metadata update + if (action.command === 'isolate' && !endpointResponse.error) { + for (const agentId of action.agents) { + await Promise.all([ + sendEndpointMetadataUpdate(esClient, agentId, { + Endpoint: { + state: { + isolation: true, + }, + }, + }), + + checkInFleetAgent(esClient, agentId), + ]); + } + } + + // For UnIsolate, if response is not an Error, then also send metadata update + if (action.command === 'unisolate' && !endpointResponse.error) { + for (const agentId of action.agents) { + await Promise.all([ + sendEndpointMetadataUpdate(esClient, agentId, { + Endpoint: { + state: { + isolation: false, + }, + }, + }), + + checkInFleetAgent(esClient, agentId), + ]); + } + } + + // For `get-file`, upload a file to ES + if ((action.command === 'execute' || action.command === 'get-file') && !endpointResponse.error) { + const filePath = + action.command === 'execute' + ? '/execute/file/path' + : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ( + action as ActionDetails< + ResponseActionGetFileOutputContent, + ResponseActionGetFileParameters + > + )?.parameters?.path!; + + const fileName = basename(filePath.replace(/\\/g, '/')); + const fileMetaDoc: FileUploadMetadata = generateFileMetadataDocumentMock({ + action_id: action.id, + agent_id: action.agents[0], + contents: [ + { + sha256: '8d61673c9d782297b3c774ded4e3d88f31a8869a8f25cf5cdd402ba6822d1d28', + file_name: fileName ?? 'bad_file.txt', + path: filePath, + size: 4, + type: 'file', + }, + ], + file: { + attributes: ['archive', 'compressed'], + ChunkSize: 4194304, + compression: 'deflate', + hash: { + sha256: '8d61673c9d782297b3c774ded4e3d88f31a8869a8f25cf5cdd402ba6822d1d28', + }, + mime_type: 'application/zip', + name: action.command === 'execute' ? 'full-output.zip' : 'upload.zip', + extension: 'zip', + size: 125, + Status: 'READY', + type: 'file', + }, + src: 'endpoint', + }); + + // Index the file's metadata + const fileMeta = await esClient.index({ + index: FILE_STORAGE_METADATA_INDEX, + id: getFileDownloadId(action, action.agents[0]), + body: fileMetaDoc, + refresh: 'wait_for', + }); + + // Index the file content (just one chunk) + // call to `.index()` copied from File plugin here: + // https://github.com/elastic/kibana/blob/main/src/plugins/files/server/blob_storage_service/adapters/es/content_stream/content_stream.ts#L195 + await esClient + .index( + { + index: FILE_STORAGE_DATA_INDEX, + id: `${fileMeta._id}.0`, + document: cborx.encode({ + bid: fileMeta._id, + last: true, + data: Buffer.from( + 'UEsDBAoACQAAAFZeRFWpAsDLHwAAABMAAAAMABwAYmFkX2ZpbGUudHh0VVQJAANTVjxjU1Y8Y3V4CwABBPUBAAAEFAAAAMOcoyEq/Q4VyG02U9O0LRbGlwP/y5SOCfRKqLz1rsBQSwcIqQLAyx8AAAATAAAAUEsBAh4DCgAJAAAAVl5EVakCwMsfAAAAEwAAAAwAGAAAAAAAAQAAAKSBAAAAAGJhZF9maWxlLnR4dFVUBQADU1Y8Y3V4CwABBPUBAAAEFAAAAFBLBQYAAAAAAQABAFIAAAB1AAAAAAA=', + 'base64' + ), + }), + refresh: 'wait_for', + }, + { + headers: { + 'content-type': 'application/cbor', + accept: 'application/json', + }, + } + ) + .then(() => sleep(2000)); + } + + return endpointResponse; +}; +type ResponseOutput = Pick< + LogsEndpointActionResponse['EndpointActions']['data'], + 'output' +>; +const getOutputDataIfNeeded = (action: ActionDetails): ResponseOutput => { + const commentUppercase = (action?.comment ?? '').toUpperCase(); + + switch (action.command) { + case 'running-processes': + return { + output: { + type: 'json', + content: { + entries: endpointActionGenerator.randomResponseActionProcesses(100), + }, + }, + } as ResponseOutput; + + case 'get-file': + return { + output: { + type: 'json', + content: { + code: 'ra_get-file_success_done', + zip_size: 123, + contents: [ + { + type: 'file', + path: ( + action as ActionDetails< + ResponseActionGetFileOutputContent, + ResponseActionGetFileParameters + > + ).parameters?.path, + size: 1234, + file_name: 'bad_file.txt', + sha256: '9558c5cb39622e9b3653203e772b129d6c634e7dbd7af1b244352fc1d704601f', + }, + ], + }, + }, + } as ResponseOutput; + + case 'execute': + const executeOutput: Partial = { + output_file_id: getFileDownloadId(action, action.agents[0]), + }; + + // Error? + if (commentUppercase.indexOf('EXECUTE:FAILURE') > -1) { + executeOutput.stdout = ''; + executeOutput.stdout_truncated = false; + executeOutput.output_file_stdout_truncated = false; + } else { + executeOutput.stderr = ''; + executeOutput.stderr_truncated = false; + executeOutput.output_file_stderr_truncated = false; + } + + return { + output: endpointActionGenerator.generateExecuteActionResponseOutput({ + content: executeOutput, + }), + } as ResponseOutput; + + default: + return { output: undefined }; + } +}; diff --git a/x-pack/plugins/security_solution/server/lib/machine_learning/index.ts b/x-pack/plugins/security_solution/server/lib/machine_learning/index.ts index d9944df4ad0ba..e308619ed0f47 100644 --- a/x-pack/plugins/security_solution/server/lib/machine_learning/index.ts +++ b/x-pack/plugins/security_solution/server/lib/machine_learning/index.ts @@ -7,7 +7,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import type { MlAnomalyRecordDoc as Anomaly } from '@kbn/ml-plugin/server'; +import type { MlAnomalyRecordDoc as Anomaly } from '@kbn/ml-anomaly-utils'; import type { Filter } from '@kbn/es-query'; export type { Anomaly }; diff --git a/x-pack/plugins/security_solution/tsconfig.json b/x-pack/plugins/security_solution/tsconfig.json index 748130f6be7b1..5ab440f404d80 100644 --- a/x-pack/plugins/security_solution/tsconfig.json +++ b/x-pack/plugins/security_solution/tsconfig.json @@ -156,5 +156,6 @@ "@kbn/core-lifecycle-browser", "@kbn/ecs", "@kbn/url-state", + "@kbn/ml-anomaly-utils" ] } diff --git a/x-pack/plugins/serverless/kibana.jsonc b/x-pack/plugins/serverless/kibana.jsonc index af79877f1079f..5a724f7641a6a 100644 --- a/x-pack/plugins/serverless/kibana.jsonc +++ b/x-pack/plugins/serverless/kibana.jsonc @@ -14,8 +14,9 @@ ], "requiredPlugins": [ "kibanaReact", + "management", ], "optionalPlugins": [], "requiredBundles": [] } -} \ No newline at end of file +} diff --git a/x-pack/plugins/serverless/public/plugin.tsx b/x-pack/plugins/serverless/public/plugin.tsx index 9c9debe3e9f21..20f4634fb7fa0 100644 --- a/x-pack/plugins/serverless/public/plugin.tsx +++ b/x-pack/plugins/serverless/public/plugin.tsx @@ -13,23 +13,43 @@ import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/cor import { ProjectSwitcher, ProjectSwitcherKibanaProvider } from '@kbn/serverless-project-switcher'; import { ProjectType } from '@kbn/serverless-types'; -import { ServerlessPluginSetup, ServerlessPluginStart } from './types'; +import { + ServerlessPluginSetup, + ServerlessPluginStart, + ServerlessPluginSetupDependencies, + ServerlessPluginStartDependencies, +} from './types'; import { ServerlessConfig } from './config'; import { API_SWITCH_PROJECT as projectChangeAPIUrl } from '../common'; -export class ServerlessPlugin implements Plugin { +export class ServerlessPlugin + implements + Plugin< + ServerlessPluginSetup, + ServerlessPluginStart, + ServerlessPluginSetupDependencies, + ServerlessPluginStartDependencies + > +{ private readonly config: ServerlessConfig; constructor(private readonly initializerContext: PluginInitializerContext) { this.config = this.initializerContext.config.get(); } - public setup(_core: CoreSetup): ServerlessPluginSetup { + public setup( + _core: CoreSetup, + _dependencies: ServerlessPluginSetupDependencies + ): ServerlessPluginSetup { return {}; } - public start(core: CoreStart): ServerlessPluginStart { + public start( + core: CoreStart, + dependencies: ServerlessPluginStartDependencies + ): ServerlessPluginStart { const { developer } = this.config; + const { management } = dependencies; if (developer && developer.projectSwitcher && developer.projectSwitcher.enabled) { const { currentType } = developer.projectSwitcher; @@ -40,6 +60,7 @@ export class ServerlessPlugin implements Plugin +{ + public setup( + _core: CoreSetup, + _setupDeps: ServerlessSecurityPluginSetupDependencies + ): ServerlessSecurityPluginSetup { + return {}; + } + + public start( + _core: CoreStart, + { securitySolution }: ServerlessSecurityPluginStartDependencies + ): ServerlessSecurityPluginStart { + securitySolution.setIsSidebarEnabled(false); + return {}; + } + + public stop() {} +} diff --git a/x-pack/plugins/serverless_security/public/types.ts b/x-pack/plugins/serverless_security/public/types.ts new file mode 100644 index 0000000000000..1fc18893ce1fd --- /dev/null +++ b/x-pack/plugins/serverless_security/public/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 { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/public'; +import { + PluginSetup as SecuritySolutionPluginSetup, + PluginStart as SecuritySolutionPluginStart, +} from '@kbn/security-solution-plugin/public'; +import { ServerlessPluginSetup, ServerlessPluginStart } from '@kbn/serverless/public'; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessSecurityPluginSetup {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessSecurityPluginStart {} + +export interface ServerlessSecurityPluginSetupDependencies { + security: SecurityPluginSetup; + securitySolution: SecuritySolutionPluginSetup; + serverless: ServerlessPluginSetup; +} + +export interface ServerlessSecurityPluginStartDependencies { + security: SecurityPluginStart; + securitySolution: SecuritySolutionPluginStart; + serverless: ServerlessPluginStart; +} diff --git a/x-pack/plugins/serverless_security/server/config.ts b/x-pack/plugins/serverless_security/server/config.ts new file mode 100644 index 0000000000000..758b22de1514e --- /dev/null +++ b/x-pack/plugins/serverless_security/server/config.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; +import { PluginConfigDescriptor } from '@kbn/core/server'; + +export * from './types'; + +const configSchema = schema.object({ + enabled: schema.boolean({ defaultValue: false }), +}); + +type ConfigType = TypeOf; + +export const config: PluginConfigDescriptor = { + schema: configSchema, +}; + +export type ServerlessSecurityConfig = TypeOf; diff --git a/x-pack/plugins/serverless_security/server/index.ts b/x-pack/plugins/serverless_security/server/index.ts new file mode 100644 index 0000000000000..b2b6c8564f788 --- /dev/null +++ b/x-pack/plugins/serverless_security/server/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { PluginInitializerContext } from '@kbn/core/server'; + +import { ServerlessSecurityPlugin } from './plugin'; +export { config } from './config'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. + +export function plugin(initializerContext: PluginInitializerContext) { + return new ServerlessSecurityPlugin(initializerContext); +} + +export type { ServerlessSecurityPluginSetup, ServerlessSecurityPluginStart } from './types'; diff --git a/x-pack/plugins/serverless_security/server/plugin.ts b/x-pack/plugins/serverless_security/server/plugin.ts new file mode 100644 index 0000000000000..7dfbae3b64a67 --- /dev/null +++ b/x-pack/plugins/serverless_security/server/plugin.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 { PluginInitializerContext, Plugin } from '@kbn/core/server'; + +import { + ServerlessSecurityPluginSetup, + ServerlessSecurityPluginStart, + ServerlessSecurityPluginSetupDependencies, + ServerlessSecurityPluginStartDependencies, +} from './types'; + +export class ServerlessSecurityPlugin + implements + Plugin< + ServerlessSecurityPluginSetup, + ServerlessSecurityPluginStart, + ServerlessSecurityPluginSetupDependencies, + ServerlessSecurityPluginStartDependencies + > +{ + constructor(_initializerContext: PluginInitializerContext) {} + + public setup() { + return {}; + } + + public start() { + return {}; + } + + public stop() {} +} diff --git a/x-pack/plugins/serverless_security/server/types.ts b/x-pack/plugins/serverless_security/server/types.ts new file mode 100644 index 0000000000000..be7167d030315 --- /dev/null +++ b/x-pack/plugins/serverless_security/server/types.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 { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/server'; +import { + PluginSetup as SecuritySolutionPluginSetup, + PluginStart as SecuritySolutionPluginStart, +} from '@kbn/security-solution-plugin/server'; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessSecurityPluginSetup {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessSecurityPluginStart {} + +export interface ServerlessSecurityPluginSetupDependencies { + security: SecurityPluginSetup; + securitySolution: SecuritySolutionPluginSetup; +} + +export interface ServerlessSecurityPluginStartDependencies { + security: SecurityPluginStart; + securitySolution: SecuritySolutionPluginStart; +} diff --git a/x-pack/plugins/serverless_security/tsconfig.json b/x-pack/plugins/serverless_security/tsconfig.json new file mode 100644 index 0000000000000..ddf77b288e628 --- /dev/null +++ b/x-pack/plugins/serverless_security/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types" + }, + "include": [ + "index.ts", + "common/**/*.ts", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + "../../../typings/**/*" + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/core", + "@kbn/config-schema", + "@kbn/security-plugin", + "@kbn/security-solution-plugin", + "@kbn/serverless", + ] +} diff --git a/x-pack/plugins/stack_connectors/public/connector_types/slack/action_form.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/slack/action_form.test.tsx new file mode 100644 index 0000000000000..32eb23f9a167f --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/slack/action_form.test.tsx @@ -0,0 +1,177 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { ActionForm } from '@kbn/triggers-actions-ui-plugin/public/application/sections/action_connector_form/action_form'; +import { TypeRegistry } from '@kbn/triggers-actions-ui-plugin/public/application/type_registry'; +import { ActionTypeModel } from '@kbn/triggers-actions-ui-plugin/public/types'; +import type { ApplicationStart } from '@kbn/core/public'; +import { useKibana } from '@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'; +import { IToasts } from '@kbn/core/public'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { getConnectorType as getSlackConnectorType } from './slack'; +import { getSlackApiConnectorType } from '../slack_api'; + +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); +jest.mock('@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting', () => ({ + useUiSetting: jest.fn(() => false), + useUiSetting$: jest.fn((value: string) => ['0,0']), +})); +jest.mock('@kbn/triggers-actions-ui-plugin/public/application/lib/action_connector_api/connectors'); +jest.mock( + '@kbn/triggers-actions-ui-plugin/public/application/lib/action_connector_api/connector_types' +); +jest.mock( + '@kbn/triggers-actions-ui-plugin/public/application/lib/action_connector_api/execute', + () => ({ + executeAction: async () => ({ + status: 'ok', + data: { + ok: true, + channels: [ + { + id: 'channel-id', + name: 'channel-name', + }, + ], + }, + connector_id: '.slack_api', + }), + }) +); +const { loadAllActions } = jest.requireMock( + '@kbn/triggers-actions-ui-plugin/public/application/lib/action_connector_api/connectors' +); +const { loadActionTypes } = jest.requireMock( + '@kbn/triggers-actions-ui-plugin/public/application/lib/action_connector_api/connector_types' +); +const useKibanaMock = useKibana as jest.Mocked; + +// GET api/actions/connector_types?feature_id=alerting +loadActionTypes.mockResolvedValue([ + { + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + id: '.slack', + minimumLicenseRequired: 'basic', + name: 'Slack', + supportedFeatureIds: ['alerting', 'uptime', 'siem'], + }, + { + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + id: '.slack_api', + minimumLicenseRequired: 'basic', + name: 'Slack API', + supportedFeatureIds: ['alerting', 'siem'], + }, +]); + +// GET api/actions/connectors +loadAllActions.mockResolvedValue([ + { + actionTypeId: '.slack_api', + config: {}, + id: 'connector-id', + isDeprecated: false, + isMissingSecrets: false, + isPreconfigured: false, + name: 'webapi', + referencedByCount: 0, + }, +]); + +const actionTypeRegistry = new TypeRegistry(); +actionTypeRegistry.register(getSlackConnectorType()); +actionTypeRegistry.register(getSlackApiConnectorType()); + +const baseProps = { + actions: [], + defaultActionGroupId: 'metrics.inventory_threshold.fired', + hasSummary: true, + featureId: 'alerting', + recoveryActionGroup: 'recovered', + actionTypeRegistry, + minimumThrottleInterval: [1, 'm'] as [number | undefined, string], + setActions: jest.fn(), + setActionIdByIndex: jest.fn(), + setActionParamsProperty: jest.fn(), + setActionFrequencyProperty: jest.fn(), + setActionAlertsFilterProperty: jest.fn(), +}; + +const mockToasts = { + danger: jest.fn(), + warning: jest.fn(), +}; + +jest.mock('@kbn/triggers-actions-ui-plugin/public', () => { + const original = jest.requireActual('@kbn/triggers-actions-ui-plugin/public'); + return { + ...original, + useKibana: () => ({ + ...original.useKibana(), + notifications: { toasts: mockToasts }, + }), + }; +}); + +describe('ActionForm - Slack API Connector', () => { + beforeAll(() => { + useKibanaMock().services.notifications.toasts = { + addSuccess: jest.fn(), + addError: jest.fn(), + addDanger: jest.fn(), + } as unknown as IToasts; + + useKibanaMock().services.application.capabilities = { + actions: { + delete: true, + save: true, + show: true, + }, + } as unknown as ApplicationStart['capabilities']; + }); + + test('show error message when no channel has been selected', async () => { + const testActions = [ + { + id: 'connector-id', + actionTypeId: '.slack_api', + group: 'metrics.inventory_threshold.fired', + params: { + subAction: 'postMessage', + subActionParams: { + text: 'text', + channels: [], // no channel selected + }, + }, + frequency: { + notifyWhen: 'onActionGroupChange' as 'onActionGroupChange', + throttle: null, + summary: false, + }, + }, + ]; + + const testProps = { + ...baseProps, + actions: testActions, + }; + + render( + + + + ); + + expect(await screen.findByText('Selected channel is required.')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/alert_rules/default_status_alert.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/alert_rules/default_status_alert.journey.ts index b9fc467fc0bef..2373b9712a753 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/alert_rules/default_status_alert.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/alert_rules/default_status_alert.journey.ts @@ -58,6 +58,7 @@ journey(`DefaultStatusAlert`, async ({ page, params }) => { await page.isDisabled(byTestId('xpack.synthetics.toggleAlertFlyout')); await page.click(byTestId('xpack.synthetics.toggleAlertFlyout')); await page.waitForSelector('text=Edit rule'); + expect(await page.locator(`[data-test-subj="intervalFormRow"]`).count()).toEqual(0); await page.click(byTestId('saveEditedRuleButton')); await page.waitForSelector("text=Updated 'Synthetics internal alert'"); }); diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/management_list.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/management_list.journey.ts index 357ab213819eb..d4110ca7508c3 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/management_list.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/management_list.journey.ts @@ -25,7 +25,7 @@ journey(`MonitorManagementList`, async ({ page, params }) => { const pageBaseUrl = 'http://localhost:5620/app/synthetics/monitors'; const searchBarInput = page.locator( - '[placeholder="Search by name, url, host, tag, project or location"]' + '[placeholder="Search by name, URL, host, tag, project or location"]' ); page.setDefaultTimeout(60 * 1000); @@ -72,12 +72,12 @@ journey(`MonitorManagementList`, async ({ page, params }) => { await page.click('span >> text="Journey / Page"'); await page.click('[aria-label="Apply the selected filters for Type"]'); expect(page.url()).toBe(`${pageBaseUrl}?monitorTypes=%5B%22browser%22%5D`); - await page.click('[placeholder="Search by name, url, host, tag, project or location"]'); + await page.click('[placeholder="Search by name, URL, host, tag, project or location"]'); await Promise.all([ page.waitForNavigation({ url: `${pageBaseUrl}?monitorTypes=%5B%22browser%22%5D&query=3`, }), - page.fill('[placeholder="Search by name, url, host, tag, project or location"]', '3'), + page.fill('[placeholder="Search by name, URL, host, tag, project or location"]', '3'), ]); await page.click('text=1-1'); await page.waitForSelector('text=Showing 1-1 of 1 Configuration'); diff --git a/x-pack/plugins/synthetics/kibana.jsonc b/x-pack/plugins/synthetics/kibana.jsonc index 1cc3b45f4637d..9ed7f52cb67c7 100644 --- a/x-pack/plugins/synthetics/kibana.jsonc +++ b/x-pack/plugins/synthetics/kibana.jsonc @@ -40,7 +40,6 @@ "fleet", "kibanaReact", "kibanaUtils", - "ml", "observability", "spaces", "indexLifecycleManagement" diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/alerts/toggle_alert_flyout_button.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/alerts/toggle_alert_flyout_button.tsx index 0ff7d0518e7d6..96c00d08843b5 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/alerts/toggle_alert_flyout_button.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/alerts/toggle_alert_flyout_button.tsx @@ -100,8 +100,8 @@ export const ToggleAlertFlyoutButton = () => { }; const noWritePermissionsTooltipContent = i18n.translate( - 'xpack.synthetics.alertDropdown.noWritePermissions', + 'xpack.synthetics.alertDropdown.noPermissions', { - defaultMessage: 'You need read-write access to Uptime to create alerts in this app.', + defaultMessage: 'You do not have sufficient permissions to perform this action.', } ); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_details_panel.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_details_panel.tsx index 5cddb55074c24..dffc3b1f357b6 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_details_panel.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_details_panel.tsx @@ -235,7 +235,7 @@ const TAGS_LABEL = i18n.translate('xpack.synthetics.management.monitorList.tags' }); const ENABLED_LABEL = i18n.translate('xpack.synthetics.detailsPanel.monitorDetails.enabled', { - defaultMessage: 'Enabled', + defaultMessage: 'Enabled (all locations)', }); const MONITOR_TYPE_LABEL = i18n.translate( diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/monitor_test_result/browser_steps_list.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/monitor_test_result/browser_steps_list.tsx index ccec4c1e8f60d..fc534b1408f39 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/monitor_test_result/browser_steps_list.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/monitor_test_result/browser_steps_list.tsx @@ -306,6 +306,7 @@ const StyleForStepStatus = ({ @@ -70,6 +70,13 @@ const SELECT_ONE_OR_MORE_LOCATIONS = i18n.translate( } ); +const SELECT_ONE_OR_MORE_LOCATIONS_DETAILS = i18n.translate( + 'xpack.synthetics.monitorManagement.selectOneOrMoreLocationsDetails', + { + defaultMessage: 'Select locations where monitors will be executed.', + } +); + const LOCATIONS_LABEL = i18n.translate('xpack.synthetics.monitorManagement.locationsLabel', { defaultMessage: 'Locations', }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/steps/read_only_callout.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/steps/read_only_callout.tsx index 6a3d2fda361f0..d20453be1c2ed 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/steps/read_only_callout.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/steps/read_only_callout.tsx @@ -22,7 +22,7 @@ export const ReadOnlyCallout = ({ projectId }: { projectId?: string }) => {

{projectId} }} />

diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/search_field.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/search_field.tsx index 01e9beda8a7e1..5ff1eaf742fca 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/search_field.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/common/search_field.tsx @@ -56,5 +56,5 @@ export function SearchField() { } const PLACEHOLDER_TEXT = i18n.translate('xpack.synthetics.monitorManagement.filter.placeholder', { - defaultMessage: `Search by name, url, host, tag, project or location`, + defaultMessage: `Search by name, URL, host, tag, project or location`, }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/labels.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/labels.ts index ff297267dcb62..45100e71fda61 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/labels.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/labels.ts @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; export const LOADING_LABEL = i18n.translate( 'xpack.synthetics.monitorManagement.manageMonitorLoadingLabel', { - defaultMessage: 'Loading Monitor Management', + defaultMessage: 'Loading Synthetics App', } ); @@ -24,14 +24,14 @@ export const LEARN_MORE_LABEL = i18n.translate( export const CALLOUT_MANAGEMENT_DISABLED = i18n.translate( 'xpack.synthetics.monitorManagement.callout.disabled', { - defaultMessage: 'Monitor Management is currently disabled', + defaultMessage: 'Synthetics App is currently disabled', } ); export const CALLOUT_MANAGEMENT_CONTACT_ADMIN = i18n.translate( 'xpack.synthetics.monitorManagement.callout.disabled.adminContact', { - defaultMessage: 'Monitor Management will be enabled when an admin visits the Synthetics app.', + defaultMessage: 'Synthetics App will be enabled when an admin visits the Synthetics app.', } ); @@ -39,28 +39,28 @@ export const CALLOUT_MANAGEMENT_DESCRIPTION = i18n.translate( 'xpack.synthetics.monitorManagement.callout.description.disabled', { defaultMessage: - "Monitor Management requires a valid API key to run your monitors on Elastic's global managed testing locations. If you already had enabled Monitor Management previously, the API key may no longer be valid.", + "Synthetics App requires a valid API key to run your monitors on Elastic's global managed testing locations. If you already had enabled Synthetics App previously, the API key may no longer be valid.", } ); export const ERROR_HEADING_BODY = i18n.translate( 'xpack.synthetics.monitorManagement.editMonitorError.description', { - defaultMessage: 'Monitor Management settings could not be loaded. Please contact Support.', + defaultMessage: 'Synthetics App settings could not be loaded. Please contact Support.', } ); export const SYNTHETICS_ENABLE_LABEL = i18n.translate( 'xpack.synthetics.monitorManagement.syntheticsEnableLabel.management', { - defaultMessage: 'Enable Monitor Management', + defaultMessage: 'Enable Synthetics App', } ); export const ERROR_HEADING_LABEL = i18n.translate( 'xpack.synthetics.monitorManagement.editMonitorError', { - defaultMessage: 'Error loading Monitor Management', + defaultMessage: 'Error loading Synthetics App', } ); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_errors/monitor_async_error.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_errors/monitor_async_error.tsx index d6dbf0465b066..ae5a035b76b54 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_errors/monitor_async_error.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/monitor_errors/monitor_async_error.tsx @@ -24,7 +24,7 @@ export const MonitorAsyncError = () => { title={ } color="warning" @@ -33,7 +33,7 @@ export const MonitorAsyncError = () => {

    diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/labels.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/labels.ts index 961e0c3782e81..506e446fe8a74 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/labels.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/labels.ts @@ -7,47 +7,25 @@ import { i18n } from '@kbn/i18n'; -export const SYNTHETICS_ENABLE_SUCCESS = i18n.translate( - 'xpack.synthetics.monitorManagement.syntheticsEnableSuccess', +export const SYNTHETICS_APP_ENABLEMENT_TITLE = i18n.translate( + 'xpack.synthetics.emptyState.enablement.title', { - defaultMessage: 'Monitor Management enabled successfully.', + defaultMessage: 'Monitor the status of your services and applications with Synthetics', } ); -export const SYNTHETICS_DISABLE_SUCCESS = i18n.translate( - 'xpack.synthetics.monitorManagement.syntheticsDisabledSuccess', - { - defaultMessage: 'Monitor Management disabled successfully.', - } -); - -export const MONITOR_MANAGEMENT_ENABLEMENT_LABEL = i18n.translate( - 'xpack.synthetics.monitorManagement.emptyState.enablement.enabled.title', - { - defaultMessage: 'Enable Monitor Management', - } -); - -export const SYNTHETICS_APP_DISABLED_LABEL = i18n.translate( - 'xpack.synthetics.emptyState.enablement.disabled.title', - { - defaultMessage: 'Synthetics App is disabled', - } -); - -export const MONITOR_MANAGEMENT_ENABLEMENT_MESSAGE = i18n.translate( - 'xpack.synthetics.monitorManagement.emptyState.enablement', +export const MONITOR_MANAGEMENT_DISABLED_MESSAGE = i18n.translate( + 'xpack.synthetics.emptyState.enablement.disabledDescription', { defaultMessage: - 'Enable Monitor Management to run lightweight and real-browser monitors from hosted testing locations around the world. Enabling Monitor Management will generate an API key to allow the Synthetics Service to write back to your Elasticsearch cluster.', + 'Run automated checks based on real-browser simulations and lightweight endpoint pings to measure the experience of your users from any location worldwide.', } ); -export const MONITOR_MANAGEMENT_DISABLED_MESSAGE = i18n.translate( - 'xpack.synthetics.emptyState.enablement.disabledDescription', +export const MONITOR_MANAGEMENT_CONTACT_ADMINISTRATOR = i18n.translate( + 'xpack.synthetics.emptyState.enablement.contactAdministrator', { - defaultMessage: - 'Synthetics App is currently disabled. Synthetics App allows you to run lightweight and real-browser monitors from hosted testing locations around the world. To enable Synthetics App, please contact an administrator.', + defaultMessage: 'Only administrators can enable this feature.', } ); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/synthetics_enablement.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/synthetics_enablement.tsx index e4fcee12f65a5..4324bba250a1a 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/synthetics_enablement.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/synthetics_enablement.tsx @@ -5,55 +5,24 @@ * 2.0. */ -import React, { useState, useEffect, useRef } from 'react'; +import React from 'react'; import { EuiEmptyPrompt, EuiTitle, EuiLink } from '@elastic/eui'; import { useEnablement } from '../../../../hooks/use_enablement'; -import { kibanaService } from '../../../../../../utils/kibana_service'; import * as labels from './labels'; export const EnablementEmptyState = () => { - const { error, enablement, loading } = useEnablement(); - const [shouldFocusEnablementButton, setShouldFocusEnablementButton] = useState(false); - const [isEnabling, setIsEnabling] = useState(false); + const { enablement, loading } = useEnablement(); const { isEnabled } = enablement; - const isEnabledRef = useRef(isEnabled); - const buttonRef = useRef(null); - - useEffect(() => { - if (!isEnabled && isEnabledRef.current === true) { - /* shift focus to enable button when enable toggle disappears. Prevent - * focus loss on the page */ - setShouldFocusEnablementButton(true); - } - isEnabledRef.current = Boolean(isEnabled); - }, [isEnabled]); - - useEffect(() => { - if (isEnabling && isEnabled) { - setIsEnabling(false); - kibanaService.toasts.addSuccess({ - title: labels.SYNTHETICS_ENABLE_SUCCESS, - toastLifeTimeMs: 3000, - }); - } else if (isEnabling && error) { - setIsEnabling(false); - kibanaService.toasts.addSuccess({ - title: labels.SYNTHETICS_DISABLE_SUCCESS, - toastLifeTimeMs: 3000, - }); - } - }, [isEnabled, isEnabling, error]); - - useEffect(() => { - if (shouldFocusEnablementButton) { - buttonRef.current?.focus(); - } - }, [shouldFocusEnablementButton]); return !isEnabled && !loading ? ( {labels.SYNTHETICS_APP_DISABLED_LABEL}} - body={

    {labels.MONITOR_MANAGEMENT_DISABLED_MESSAGE}

    } + title={

    {labels.SYNTHETICS_APP_ENABLEMENT_TITLE}

    } + body={ + <> +

    {labels.MONITOR_MANAGEMENT_DISABLED_MESSAGE}

    +

    {labels.MONITOR_MANAGEMENT_CONTACT_ADMINISTRATOR}

    + + } footer={ <> diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/actions_popover.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/actions_popover.test.tsx index 99f199b52498e..ce8d29ca345c4 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/actions_popover.test.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/actions_popover.test.tsx @@ -140,7 +140,7 @@ describe('ActionsPopover', () => { locationId={testMonitor.location.id} /> ); - const enableButton = getByText('Disable monitor'); + const enableButton = getByText('Disable monitor (all locations)'); fireEvent.click(enableButton); expect(updateMonitorEnabledState).toHaveBeenCalledTimes(1); expect(updateMonitorEnabledState.mock.calls[0]).toEqual([false]); @@ -162,7 +162,7 @@ describe('ActionsPopover', () => { locationId={testMonitor.location.id} /> ); - const enableButton = getByText('Enable monitor'); + const enableButton = getByText('Enable monitor (all locations)'); fireEvent.click(enableButton); expect(updateMonitorEnabledState).toHaveBeenCalledTimes(1); expect(updateMonitorEnabledState.mock.calls[0]).toEqual([true]); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/actions_popover.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/actions_popover.tsx index 90822b63dccfa..26e752f62a274 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/actions_popover.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/actions_popover.tsx @@ -350,28 +350,28 @@ const loadingLabel = (isEnabled: boolean) => const enableMonitorLabel = i18n.translate( 'xpack.synthetics.overview.actions.enableLabelEnableMonitor', { - defaultMessage: 'Enable monitor', + defaultMessage: 'Enable monitor (all locations)', } ); const disableMonitorLabel = i18n.translate( 'xpack.synthetics.overview.actions.enableLabelDisableMonitor', { - defaultMessage: 'Disable monitor', + defaultMessage: 'Disable monitor (all locations)', } ); const disableAlertLabel = i18n.translate( 'xpack.synthetics.overview.actions.disableLabelDisableAlert', { - defaultMessage: 'Disable status alerts', + defaultMessage: 'Disable status alerts (all locations)', } ); const enableMonitorAlertLabel = i18n.translate( 'xpack.synthetics.overview.actions.enableLabelDisableAlert', { - defaultMessage: 'Enable status alerts', + defaultMessage: 'Enable status alerts (all locations)', } ); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/monitor_detail_flyout.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/monitor_detail_flyout.tsx index 33138c33851c9..18f7b25055a6b 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/monitor_detail_flyout.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/monitor_detail_flyout.tsx @@ -391,7 +391,7 @@ const PREVIOUS_PERIOD_SERIES_NAME = i18n.translate( ); const ENABLED_ITEM_TEXT = i18n.translate('xpack.synthetics.monitorList.enabledItemText', { - defaultMessage: 'Enabled', + defaultMessage: 'Enabled (all locations)', }); const CLOSE_FLYOUT_TEXT = i18n.translate('xpack.synthetics.monitorList.closeFlyoutText', { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/route_config.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/route_config.tsx index 57f40f5676593..0ac6a3ccc0f7e 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/route_config.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/route_config.tsx @@ -44,7 +44,7 @@ export const getMonitorsRoute = ( }, { title: i18n.translate('xpack.synthetics.monitorManagementRoute.title', { - defaultMessage: 'Monitor Management | {baseTitle}', + defaultMessage: 'Synthetics Management | {baseTitle}', values: { baseTitle }, }), path: MONITORS_ROUTE, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/alerting_defaults/alert_defaults_form.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/alerting_defaults/alert_defaults_form.tsx index 2c81ccf1d86e7..f87f1267efcc3 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/alerting_defaults/alert_defaults_form.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/alerting_defaults/alert_defaults_form.tsx @@ -93,7 +93,7 @@ export const AlertDefaultsForm = () => { description={ } > diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/alerting_defaults/translations.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/alerting_defaults/translations.ts index 12d30b2d6b912..a6761c4cc6994 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/alerting_defaults/translations.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/alerting_defaults/translations.ts @@ -11,7 +11,7 @@ export const alertFormI18n = { inputPlaceHolder: i18n.translate( 'xpack.synthetics.sourceConfiguration.alertDefaultForm.selectConnector', { - defaultMessage: 'Please select one or more connectors', + defaultMessage: 'Select one or more connectors', } ), emailPlaceHolder: i18n.translate( diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/delete_location.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/delete_location.tsx index 959520c911469..cfaf62f891599 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/delete_location.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/delete_location.tsx @@ -37,8 +37,8 @@ export const DeleteLocation = ({ const deleteDisabledReason = !canSaveIntegrations ? CANNOT_SAVE_INTEGRATION_LABEL : i18n.translate('xpack.synthetics.monitorManagement.cannotDelete.description', { - defaultMessage: `This location cannot be deleted, because it has {monCount, number} {monCount, plural,one {monitor} other {monitors}} running. - Please remove this location from your monitors before deleting this location.`, + defaultMessage: `You can't delete this location because it is used in {monCount, number} {monCount, plural,one {monitor} other {monitors}}. + Remove this location from all monitors first.`, values: { monCount }, }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/view_location_monitors.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/view_location_monitors.tsx index a008da59751b1..525b088f1f6c0 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/view_location_monitors.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/settings/private_locations/view_location_monitors.tsx @@ -35,7 +35,7 @@ export const ViewLocationMonitors = ({ {locationName} }} /> @@ -66,7 +66,7 @@ export const ViewLocationMonitors = ({ const VIEW_LOCATION_MONITORS = i18n.translate( 'xpack.synthetics.monitorManagement.viewLocationMonitors', { - defaultMessage: 'View location monitors', + defaultMessage: 'View monitors', } ); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/browser/browser_test_results.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/browser/browser_test_results.tsx index 7665538d7682b..6e0ebbf2d4aa8 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/browser/browser_test_results.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/browser/browser_test_results.tsx @@ -174,7 +174,7 @@ export const FAILED_TO_RUN = i18n.translate('xpack.synthetics.monitorManagement. defaultMessage: 'Failed to run steps', }); -export const ERROR_RUNNING_TEST = i18n.translate('xpack.synthetics.testRun.runErrorLabel', { +export const ERROR_RUNNING_TEST = i18n.translate('xpack.synthetics.testRun.testErrorLabel', { defaultMessage: 'Error running test', }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/hooks/use_run_once_errors.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/hooks/use_run_once_errors.ts index 9859b5b0216bc..36611914264db 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/hooks/use_run_once_errors.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/test_now_mode/hooks/use_run_once_errors.ts @@ -7,7 +7,6 @@ import { useEffect, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { kibanaService } from '../../../../../utils/kibana_service'; import { Locations, ServiceLocationErrors } from '../../monitor_add_edit/types'; export function useRunOnceErrors({ @@ -77,7 +76,7 @@ export function useRunOnceErrors({ title: RunErrorLabel, }, ] - : [{ name: 'Error', message: PushErrorService, title: PushErrorLabel }]; + : [{ name: 'Error', message: PushErrorService, title: RunErrorLabel }]; } else if (locationErrors?.length > 0) { // If only some of the locations were unsuccessful return locationErrors @@ -93,41 +92,25 @@ export function useRunOnceErrors({ return []; }, [locationsById, locationErrors, locationErrorReasons, hasBlockingError]); - useEffect(() => { - if (showErrors) { - errorMessages.forEach( - ({ name, message, title }: { name: string; message: string; title: string }) => { - kibanaService.toasts.addError({ name, message }, { title }); - } - ); - } - }, [errorMessages, showErrors]); - return { expectPings, hasBlockingError, - blockingErrorTitle: hasBlockingError ? PushErrorLabel : null, - blockingErrorMessage: hasBlockingError - ? `${PushErrorService} ${errorMessages[0]?.message}` - : null, + blockingErrorTitle: hasBlockingError ? RunErrorLabel : null, + blockingErrorMessage: hasBlockingError ? `${errorMessages[0]?.message}` : null, errorMessages, }; } -const PushErrorLabel = i18n.translate('xpack.synthetics.testRun.pushErrorLabel', { - defaultMessage: 'Push error', -}); - const RunErrorLabel = i18n.translate('xpack.synthetics.testRun.runErrorLabel', { - defaultMessage: 'Error running test', + defaultMessage: "Can't run the test now", }); const getLocationTestErrorLabel = (locationName: string, reason: string) => i18n.translate('xpack.synthetics.testRun.runErrorLocation.reason', { - defaultMessage: 'Failed to run monitor on location {locationName}. {reason}', + defaultMessage: 'Failed to run test on location {locationName}. {reason}', values: { locationName, reason }, }); const PushErrorService = i18n.translate('xpack.synthetics.testRun.pushError', { - defaultMessage: 'Failed to push the monitor to service.', + defaultMessage: 'This test cannot be executed at this time. Try again later.', }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/lib/alert_types/monitor_status.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/lib/alert_types/monitor_status.tsx index 278eb6d522db3..2c4d5055a0891 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/lib/alert_types/monitor_status.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/lib/alert_types/monitor_status.tsx @@ -41,7 +41,7 @@ export const initMonitorStatusAlertType: AlertTypeInitializer = ({ ruleParamsExpression: (paramProps: RuleTypeParamsExpressionProps) => ( ), - validate: (ruleParams: StatusRuleParams) => { + validate: (_ruleParams: StatusRuleParams) => { return { errors: {} }; }, defaultActionMessage, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/alert_rules/effects.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/alert_rules/effects.ts index e49d84ba9c6b6..fb092adbcab33 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/alert_rules/effects.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/alert_rules/effects.ts @@ -39,7 +39,7 @@ export function* updateDefaultAlertingEffect() { const successMessage = i18n.translate('xpack.synthetics.settings.enableAlerting', { defaultMessage: - 'Monitor status rule type successfully updated. Next rule alerts will take the changes into account.', + 'Monitor status rule successfully updated. Changes will take effect on the next rule execution.', }); const failureMessage = i18n.translate('xpack.synthetics.settings.enabledAlert.fail', { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/synthetics_enablement/effects.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/synthetics_enablement/effects.ts index 14c912b07ce99..a840d7d4057bc 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/synthetics_enablement/effects.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/synthetics_enablement/effects.ts @@ -29,5 +29,5 @@ export function* fetchSyntheticsEnablementEffect() { } const failureMessage = i18n.translate('xpack.synthetics.settings.enablement.fail', { - defaultMessage: 'Failed to enable Monitor Management', + defaultMessage: 'Failed to enable Synthetics App', }); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/duration_line_bar_list.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/duration_line_bar_list.tsx index 3fb96bf0336e1..29a5a817797d9 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/duration_line_bar_list.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/duration_line_bar_list.tsx @@ -8,7 +8,12 @@ import React from 'react'; import moment from 'moment'; import { AnnotationTooltipFormatter, RectAnnotation, RectAnnotationDatum } from '@elastic/charts'; -import { ANOMALY_SEVERITY, getSeverityColor, getSeverityType } from '@kbn/ml-plugin/public'; + +// Individual deep imports to not consume the whole package bundle. +import { getSeverityColor } from '@kbn/ml-anomaly-utils/get_severity_color'; +import { getSeverityType } from '@kbn/ml-anomaly-utils/get_severity_type'; +import { ML_ANOMALY_SEVERITY } from '@kbn/ml-anomaly-utils/anomaly_severity'; + import { AnnotationTooltip } from './annotation_tooltip'; interface Props { @@ -19,7 +24,7 @@ interface Props { export const DurationAnomaliesBar = ({ anomalies, hiddenLegends }: Props) => { const anomalyAnnotations: Map = new Map(); - Object.keys(ANOMALY_SEVERITY).forEach((severityLevel) => { + Object.keys(ML_ANOMALY_SEVERITY).forEach((severityLevel) => { anomalyAnnotations.set(severityLevel.toLowerCase(), { rect: [], color: '' }); }); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/anomaly_alert/anomaly_alert.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/anomaly_alert/anomaly_alert.tsx index c22aed0c8a9ce..c4f37cfa65ff2 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/anomaly_alert/anomaly_alert.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/anomaly_alert/anomaly_alert.tsx @@ -15,7 +15,11 @@ import { } from '@elastic/eui'; import { useSelector } from 'react-redux'; import React, { useEffect, useState } from 'react'; -import { getSeverityColor, getSeverity } from '@kbn/ml-plugin/public'; + +// Individual deep imports to not consume the whole package bundle. +import { getSeverity } from '@kbn/ml-anomaly-utils/get_severity'; +import { getSeverityColor } from '@kbn/ml-anomaly-utils/get_severity_color'; + import { AnomalyTranslations } from './translations'; import { AlertExpressionPopover } from '../alert_expression_popover'; import { DEFAULT_SEVERITY, SelectSeverity, SEVERITY_OPTIONS } from './select_severity'; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/anomaly_alert/select_severity.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/anomaly_alert/select_severity.tsx index 3af35148501c0..68578a1262e80 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/anomaly_alert/select_severity.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/alerts/anomaly_alert/select_severity.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiHealth, EuiSpacer, EuiSuperSelect, EuiText } from '@elastic/eui'; -import { getSeverityColor } from '@kbn/ml-plugin/public'; +import { getSeverityColor } from '@kbn/ml-anomaly-utils/get_severity_color'; const warningLabel = i18n.translate('xpack.synthetics.controls.selectSeverity.warningLabel', { defaultMessage: 'warning', diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/translations.ts b/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/translations.ts index 5f5afc048f840..8d00c6ca948da 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/translations.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/translations.ts @@ -28,7 +28,7 @@ export const alertFormI18n = { inputPlaceHolder: i18n.translate( 'xpack.synthetics.sourceConfiguration.alertDefaultForm.selectConnector', { - defaultMessage: 'Please select one or more connectors', + defaultMessage: 'Select one or more connectors', } ), emailPlaceHolder: i18n.translate( diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/ml_anomaly.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/ml_anomaly.ts index 9f3ddff0e9178..bfb46f0d0ff64 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/ml_anomaly.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/actions/ml_anomaly.ts @@ -6,11 +6,8 @@ */ import { createAction } from 'redux-actions'; -import { - MlCapabilitiesResponse, - AnomaliesTableRecord, - JobExistResult, -} from '@kbn/ml-plugin/public'; +import type { MlAnomaliesTableRecord } from '@kbn/ml-anomaly-utils'; +import type { MlCapabilitiesResponse, JobExistResult } from '@kbn/ml-plugin/public'; import { createAsyncAction } from './utils'; import { CreateMLJobSuccess, @@ -46,7 +43,7 @@ export interface AnomalyRecordsParams { } export interface AnomalyRecords { - anomalies: AnomaliesTableRecord[]; + anomalies: MlAnomaliesTableRecord[]; interval: string; } diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/duration_anomaly.test.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/duration_anomaly.test.ts index 9203a1c7eff10..f38f5c1e61e29 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/duration_anomaly.test.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/duration_anomaly.test.ts @@ -11,17 +11,20 @@ import { } from '@kbn/rule-data-utils'; import { durationAnomalyAlertFactory } from './duration_anomaly'; import { DURATION_ANOMALY } from '../../../../common/constants/uptime_alerts'; -import { AnomaliesTableRecord, AnomalyRecordDoc } from '@kbn/ml-plugin/common/types/anomalies'; +import { + getSeverityType, + type MlAnomaliesTableRecord, + type MlAnomalyRecordDoc, +} from '@kbn/ml-anomaly-utils'; import { createRuleTypeMocks, bootstrapDependencies } from './test_utils'; -import { getSeverityType } from '@kbn/ml-plugin/common/util/anomaly_utils'; import { Ping } from '../../../../common/runtime_types/ping'; interface MockAnomaly { - severity: AnomaliesTableRecord['severity']; - source: Partial; - actualSort: AnomaliesTableRecord['actualSort']; - typicalSort: AnomaliesTableRecord['typicalSort']; - entityValue: AnomaliesTableRecord['entityValue']; + severity: MlAnomaliesTableRecord['severity']; + source: Partial; + actualSort: MlAnomaliesTableRecord['actualSort']; + typicalSort: MlAnomaliesTableRecord['typicalSort']; + entityValue: MlAnomaliesTableRecord['entityValue']; } interface MockAnomalyResult { diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/duration_anomaly.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/duration_anomaly.ts index 42d81f6e0d0d6..42c6b0e0b512a 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/duration_anomaly.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/duration_anomaly.ts @@ -13,8 +13,7 @@ import { ALERT_REASON, } from '@kbn/rule-data-utils'; import { ActionGroupIdsOf } from '@kbn/alerting-plugin/common'; -import { AnomaliesTableRecord } from '@kbn/ml-plugin/common/types/anomalies'; -import { getSeverityType } from '@kbn/ml-plugin/common/util/anomaly_utils'; +import { getSeverityType, type MlAnomaliesTableRecord } from '@kbn/ml-anomaly-utils'; import { UptimeEsClient } from '../lib'; import { updateState, @@ -37,7 +36,7 @@ import { ALERT_REASON_MSG, ACTION_VARIABLES, VIEW_IN_APP_URL } from './action_va export type ActionGroupIds = ActionGroupIdsOf; -export const getAnomalySummary = (anomaly: AnomaliesTableRecord, monitorInfo: Ping) => { +export const getAnomalySummary = (anomaly: MlAnomaliesTableRecord, monitorInfo: Ping) => { return { severity: getSeverityType(anomaly.severity), severityScore: Math.round(anomaly.severity), diff --git a/x-pack/plugins/synthetics/server/synthetics_service/get_api_key.ts b/x-pack/plugins/synthetics/server/synthetics_service/get_api_key.ts index 79af4d6cfc718..e92f5956741b5 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/get_api_key.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/get_api_key.ts @@ -126,7 +126,7 @@ export const generateAPIKey = async ({ /* Not exposed to the user. May grant as internal user */ return security.authc.apiKeys?.grantAsInternalUser(request, { - name: 'synthetics-api-key (required for monitor management)', + name: 'synthetics-api-key (required for Synthetics App)', role_descriptors: { synthetics_writer: serviceApiKeyPrivileges, }, diff --git a/x-pack/plugins/synthetics/tsconfig.json b/x-pack/plugins/synthetics/tsconfig.json index 61b7fe8d8d19c..03749d5510b64 100644 --- a/x-pack/plugins/synthetics/tsconfig.json +++ b/x-pack/plugins/synthetics/tsconfig.json @@ -79,6 +79,7 @@ "@kbn/exploratory-view-plugin", "@kbn/observability-shared-plugin", "@kbn/ml-error-utils", + "@kbn/ml-anomaly-utils", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/task_manager/server/config.test.ts b/x-pack/plugins/task_manager/server/config.test.ts index 54e9562ec62fb..9e1e89e68624b 100644 --- a/x-pack/plugins/task_manager/server/config.test.ts +++ b/x-pack/plugins/task_manager/server/config.test.ts @@ -44,6 +44,7 @@ describe('config validation', () => { "exclude_task_types": Array [], }, "version_conflict_threshold": 80, + "worker_utilization_running_average_window": 5, } `); }); @@ -95,6 +96,7 @@ describe('config validation', () => { "exclude_task_types": Array [], }, "version_conflict_threshold": 80, + "worker_utilization_running_average_window": 5, } `); }); @@ -149,6 +151,7 @@ describe('config validation', () => { "exclude_task_types": Array [], }, "version_conflict_threshold": 80, + "worker_utilization_running_average_window": 5, } `); }); diff --git a/x-pack/plugins/task_manager/server/config.ts b/x-pack/plugins/task_manager/server/config.ts index 40b915e8979a2..ae6c7a9fb41eb 100644 --- a/x-pack/plugins/task_manager/server/config.ts +++ b/x-pack/plugins/task_manager/server/config.ts @@ -18,9 +18,12 @@ export const DEFAULT_MAX_EPHEMERAL_REQUEST_CAPACITY = MAX_WORKERS_LIMIT; // =================== // Refresh aggregated monitored stats at a default rate of once a minute export const DEFAULT_MONITORING_REFRESH_RATE = 60 * 1000; -export const DEFAULT_MONITORING_STATS_RUNNING_AVERGAE_WINDOW = 50; +export const DEFAULT_MONITORING_STATS_RUNNING_AVERAGE_WINDOW = 50; export const DEFAULT_MONITORING_STATS_WARN_DELAYED_TASK_START_IN_SECONDS = 60; +// At the default poll interval of 3sec, this averages over the last 15sec. +export const DEFAULT_WORKER_UTILIZATION_RUNNING_AVERAGE_WINDOW = 5; + export const taskExecutionFailureThresholdSchema = schema.object( { error_threshold: schema.number({ @@ -98,7 +101,7 @@ export const configSchema = schema.object( }), /* The size of the running average window for monitored stats. */ monitored_stats_running_average_window: schema.number({ - defaultValue: DEFAULT_MONITORING_STATS_RUNNING_AVERGAE_WINDOW, + defaultValue: DEFAULT_MONITORING_STATS_RUNNING_AVERAGE_WINDOW, max: 100, min: 10, }), @@ -130,6 +133,11 @@ export const configSchema = schema.object( }), }), event_loop_delay: eventLoopDelaySchema, + worker_utilization_running_average_window: schema.number({ + defaultValue: DEFAULT_WORKER_UTILIZATION_RUNNING_AVERAGE_WINDOW, + max: 100, + min: 1, + }), /* These are not designed to be used by most users. Please use caution when changing these */ unsafe: schema.object({ exclude_task_types: schema.arrayOf(schema.string(), { defaultValue: [] }), diff --git a/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.test.ts b/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.test.ts index 374a52ea84b2f..c2c0f6e3aceed 100644 --- a/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.test.ts +++ b/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.test.ts @@ -77,6 +77,7 @@ describe('EphemeralTaskLifecycle', () => { monitor: true, warn_threshold: 5000, }, + worker_utilization_running_average_window: 5, ...config, }, elasticsearchAndSOAvailability$, diff --git a/x-pack/plugins/task_manager/server/integration_tests/managed_configuration.test.ts b/x-pack/plugins/task_manager/server/integration_tests/managed_configuration.test.ts index 308aa9f556797..db8981a186ab4 100644 --- a/x-pack/plugins/task_manager/server/integration_tests/managed_configuration.test.ts +++ b/x-pack/plugins/task_manager/server/integration_tests/managed_configuration.test.ts @@ -71,6 +71,7 @@ describe('managed configuration', () => { monitor: true, warn_threshold: 5000, }, + worker_utilization_running_average_window: 5, }); logger = context.logger.get('taskManager'); diff --git a/x-pack/plugins/task_manager/server/monitoring/background_task_utilization_statistics.test.ts b/x-pack/plugins/task_manager/server/monitoring/background_task_utilization_statistics.test.ts index 45b29c44b1a4b..cdd67a07ff9e7 100644 --- a/x-pack/plugins/task_manager/server/monitoring/background_task_utilization_statistics.test.ts +++ b/x-pack/plugins/task_manager/server/monitoring/background_task_utilization_statistics.test.ts @@ -9,7 +9,13 @@ import { v4 as uuidv4 } from 'uuid'; import { Subject, Observable } from 'rxjs'; import { take, bufferCount, skip, map } from 'rxjs/operators'; import { ConcreteTaskInstance, TaskStatus } from '../task'; -import { asTaskRunEvent, TaskTiming, TaskPersistence } from '../task_events'; +import { + asTaskRunEvent, + TaskTiming, + TaskPersistence, + asTaskManagerStatEvent, + TaskManagerStats, +} from '../task_events'; import { asOk } from '../lib/result_type'; import { TaskLifecycleEvent } from '../polling_lifecycle'; import { TaskRunResult } from '../task_running'; @@ -18,9 +24,10 @@ import { taskPollingLifecycleMock } from '../polling_lifecycle.mock'; import { BackgroundTaskUtilizationStat, createBackgroundTaskUtilizationAggregator, + summarizeUtilizationStats, } from './background_task_utilization_statistics'; import { AdHocTaskCounter } from '../lib/adhoc_task_counter'; -import { sum } from 'lodash'; +import { sum, mean } from 'lodash'; describe('Task Run Statistics', () => { const pollInterval = 3000; @@ -29,404 +36,586 @@ describe('Task Run Statistics', () => { jest.resetAllMocks(); }); - test('returns a running count of adhoc actual service_time', async () => { - const serviceTimes = [1000, 2000, 500, 300, 400, 15000, 20000, 200]; - const events$ = new Subject(); - const taskPollingLifecycle = taskPollingLifecycleMock.create({ - events$: events$ as Observable, - }); - const adHocTaskCounter = new AdHocTaskCounter(); - - const runningAverageWindowSize = 5; - const BackgroundTaskUtilizationAggregator = createBackgroundTaskUtilizationAggregator( - taskPollingLifecycle, - runningAverageWindowSize, - adHocTaskCounter, - pollInterval - ); - - function expectWindowEqualsUpdate( - taskStat: AggregatedStat, - window: number[] - ) { - expect(taskStat.value.adhoc.ran.service_time.actual).toEqual(sum(window)); - } - - return new Promise((resolve) => { - const events = []; - const now = Date.now(); - for (const time of serviceTimes) { - events.push({ start: runAtMillisecondsAgo(now, time).getTime(), stop: now }); + describe('createBackgroundTaskUtilizationAggregator', () => { + test('returns a running count of adhoc actual service_time', async () => { + const serviceTimes = [1000, 2000, 500, 300, 400, 15000, 20000, 200]; + const events$ = new Subject(); + const taskPollingLifecycle = taskPollingLifecycleMock.create({ + events$: events$ as Observable, + }); + const adHocTaskCounter = new AdHocTaskCounter(); + + const BackgroundTaskUtilizationAggregator = createBackgroundTaskUtilizationAggregator( + taskPollingLifecycle, + adHocTaskCounter, + pollInterval + ); + + function expectWindowEqualsUpdate( + taskStat: AggregatedStat, + window: number[] + ) { + expect(taskStat.value.adhoc.ran.service_time.actual).toEqual(sum(window)); } - BackgroundTaskUtilizationAggregator.pipe( - // skip initial stat which is just initialized data which - // ensures we don't stall on combineLatest - skip(1), - // Use 'summarizeUtilizationStat' to receive summarize stats - map(({ key, value }: AggregatedStat) => ({ - key, - value, - })), - take(serviceTimes.length), - bufferCount(serviceTimes.length) - ).subscribe((taskStats: Array>) => { - expectWindowEqualsUpdate(taskStats[0], serviceTimes.slice(0, 1)); - expectWindowEqualsUpdate(taskStats[1], serviceTimes.slice(0, 2)); - expectWindowEqualsUpdate(taskStats[2], serviceTimes.slice(0, 3)); - expectWindowEqualsUpdate(taskStats[3], serviceTimes.slice(0, 4)); - expectWindowEqualsUpdate(taskStats[4], serviceTimes.slice(0, 5)); - // from the 6th value, begin to drop old values as out window is 5 - expectWindowEqualsUpdate(taskStats[5], serviceTimes.slice(0, 6)); - expectWindowEqualsUpdate(taskStats[6], serviceTimes.slice(0, 7)); - expectWindowEqualsUpdate(taskStats[7], serviceTimes.slice(0, 8)); - resolve(); + + return new Promise((resolve) => { + const events = []; + const now = Date.now(); + for (const time of serviceTimes) { + events.push({ start: runAtMillisecondsAgo(now, time).getTime(), stop: now }); + } + BackgroundTaskUtilizationAggregator.pipe( + // skip initial stat which is just initialized data which + // ensures we don't stall on combineLatest + skip(1), + // Use 'summarizeUtilizationStat' to receive summarize stats + map(({ key, value }: AggregatedStat) => ({ + key, + value, + })), + take(serviceTimes.length), + bufferCount(serviceTimes.length) + ).subscribe((taskStats: Array>) => { + expectWindowEqualsUpdate(taskStats[0], serviceTimes.slice(0, 1)); + expectWindowEqualsUpdate(taskStats[1], serviceTimes.slice(0, 2)); + expectWindowEqualsUpdate(taskStats[2], serviceTimes.slice(0, 3)); + expectWindowEqualsUpdate(taskStats[3], serviceTimes.slice(0, 4)); + expectWindowEqualsUpdate(taskStats[4], serviceTimes.slice(0, 5)); + // from the 6th value, begin to drop old values as out window is 5 + expectWindowEqualsUpdate(taskStats[5], serviceTimes.slice(0, 6)); + expectWindowEqualsUpdate(taskStats[6], serviceTimes.slice(0, 7)); + expectWindowEqualsUpdate(taskStats[7], serviceTimes.slice(0, 8)); + resolve(); + }); + + for (const event of events) { + events$.next(mockTaskRunEvent({}, event)); + } }); + }); - for (const event of events) { - events$.next(mockTaskRunEvent({}, event)); + test('returns a running count of adhoc adjusted service_time', async () => { + const serviceTimes = [1000, 2000, 500, 300, 400, 15000, 20000, 200]; + const events$ = new Subject(); + const taskPollingLifecycle = taskPollingLifecycleMock.create({ + events$: events$ as Observable, + }); + const adHocTaskCounter = new AdHocTaskCounter(); + + const BackgroundTaskUtilizationAggregator = createBackgroundTaskUtilizationAggregator( + taskPollingLifecycle, + adHocTaskCounter, + pollInterval + ); + + function expectWindowEqualsUpdate( + taskStat: AggregatedStat, + window: number[] + ) { + expect(taskStat.value.adhoc.ran.service_time.adjusted).toEqual(sum(window)); } - }); - }); - test('returns a running count of adhoc adjusted service_time', async () => { - const serviceTimes = [1000, 2000, 500, 300, 400, 15000, 20000, 200]; - const events$ = new Subject(); - const taskPollingLifecycle = taskPollingLifecycleMock.create({ - events$: events$ as Observable, + return new Promise((resolve) => { + const events = []; + const now = Date.now(); + for (const time of serviceTimes) { + events.push({ start: runAtMillisecondsAgo(now, time).getTime(), stop: now }); + } + BackgroundTaskUtilizationAggregator.pipe( + // skip initial stat which is just initialized data which + // ensures we don't stall on combineLatest + skip(1), + // Use 'summarizeUtilizationStat' to receive summarize stats + map(({ key, value }: AggregatedStat) => ({ + key, + value, + })), + take(serviceTimes.length), + bufferCount(serviceTimes.length) + ).subscribe((taskStats: Array>) => { + expectWindowEqualsUpdate(taskStats[0], roundUpToNearestSec(serviceTimes.slice(0, 1), 3)); + expectWindowEqualsUpdate(taskStats[1], roundUpToNearestSec(serviceTimes.slice(0, 2), 3)); + expectWindowEqualsUpdate(taskStats[2], roundUpToNearestSec(serviceTimes.slice(0, 3), 3)); + expectWindowEqualsUpdate(taskStats[3], roundUpToNearestSec(serviceTimes.slice(0, 4), 3)); + expectWindowEqualsUpdate(taskStats[4], roundUpToNearestSec(serviceTimes.slice(0, 5), 3)); + // from the 6th value, begin to drop old values as out window is 5 + expectWindowEqualsUpdate(taskStats[5], roundUpToNearestSec(serviceTimes.slice(0, 6), 3)); + expectWindowEqualsUpdate(taskStats[6], roundUpToNearestSec(serviceTimes.slice(0, 7), 3)); + expectWindowEqualsUpdate(taskStats[7], roundUpToNearestSec(serviceTimes.slice(0, 8), 3)); + resolve(); + }); + + for (const event of events) { + events$.next(mockTaskRunEvent({}, event)); + } + }); }); - const adHocTaskCounter = new AdHocTaskCounter(); - - const runningAverageWindowSize = 5; - const BackgroundTaskUtilizationAggregator = createBackgroundTaskUtilizationAggregator( - taskPollingLifecycle, - runningAverageWindowSize, - adHocTaskCounter, - pollInterval - ); - - function expectWindowEqualsUpdate( - taskStat: AggregatedStat, - window: number[] - ) { - expect(taskStat.value.adhoc.ran.service_time.adjusted).toEqual(sum(window)); - } - - return new Promise((resolve) => { - const events = []; - const now = Date.now(); - for (const time of serviceTimes) { - events.push({ start: runAtMillisecondsAgo(now, time).getTime(), stop: now }); + + test('returns a running count of adhoc task_counter', async () => { + const tasks = [0, 0, 0, 0, 0, 0, 0, 0]; + const events$ = new Subject(); + const taskPollingLifecycle = taskPollingLifecycleMock.create({ + events$: events$ as Observable, + }); + const adHocTaskCounter = new AdHocTaskCounter(); + + const BackgroundTaskUtilizationAggregator = createBackgroundTaskUtilizationAggregator( + taskPollingLifecycle, + adHocTaskCounter, + pollInterval + ); + + function expectWindowEqualsUpdate( + taskStat: AggregatedStat, + window: number[] + ) { + expect(taskStat.value.adhoc.ran.service_time.task_counter).toEqual(window.length); } - BackgroundTaskUtilizationAggregator.pipe( - // skip initial stat which is just initialized data which - // ensures we don't stall on combineLatest - skip(1), - // Use 'summarizeUtilizationStat' to receive summarize stats - map(({ key, value }: AggregatedStat) => ({ - key, - value, - })), - take(serviceTimes.length), - bufferCount(serviceTimes.length) - ).subscribe((taskStats: Array>) => { - expectWindowEqualsUpdate(taskStats[0], roundUpToNearestSec(serviceTimes.slice(0, 1), 3)); - expectWindowEqualsUpdate(taskStats[1], roundUpToNearestSec(serviceTimes.slice(0, 2), 3)); - expectWindowEqualsUpdate(taskStats[2], roundUpToNearestSec(serviceTimes.slice(0, 3), 3)); - expectWindowEqualsUpdate(taskStats[3], roundUpToNearestSec(serviceTimes.slice(0, 4), 3)); - expectWindowEqualsUpdate(taskStats[4], roundUpToNearestSec(serviceTimes.slice(0, 5), 3)); - // from the 6th value, begin to drop old values as out window is 5 - expectWindowEqualsUpdate(taskStats[5], roundUpToNearestSec(serviceTimes.slice(0, 6), 3)); - expectWindowEqualsUpdate(taskStats[6], roundUpToNearestSec(serviceTimes.slice(0, 7), 3)); - expectWindowEqualsUpdate(taskStats[7], roundUpToNearestSec(serviceTimes.slice(0, 8), 3)); - resolve(); + + return new Promise((resolve) => { + BackgroundTaskUtilizationAggregator.pipe( + // skip initial stat which is just initialized data which + // ensures we don't stall on combineLatest + skip(1), + // Use 'summarizeUtilizationStat' to receive summarize stats + map(({ key, value }: AggregatedStat) => ({ + key, + value, + })), + take(tasks.length), + bufferCount(tasks.length) + ).subscribe((taskStats: Array>) => { + expectWindowEqualsUpdate(taskStats[0], tasks.slice(0, 1)); + expectWindowEqualsUpdate(taskStats[1], tasks.slice(0, 2)); + expectWindowEqualsUpdate(taskStats[2], tasks.slice(0, 3)); + expectWindowEqualsUpdate(taskStats[3], tasks.slice(0, 4)); + expectWindowEqualsUpdate(taskStats[4], tasks.slice(0, 5)); + // from the 6th value, begin to drop old values as out window is 5 + expectWindowEqualsUpdate(taskStats[5], tasks.slice(0, 6)); + expectWindowEqualsUpdate(taskStats[6], tasks.slice(0, 7)); + expectWindowEqualsUpdate(taskStats[7], tasks.slice(0, 8)); + resolve(); + }); + + for (const task of tasks) { + events$.next(mockTaskRunEvent({}, { start: task, stop: task })); + } }); + }); - for (const event of events) { - events$.next(mockTaskRunEvent({}, event)); + test('returns a running count of adhoc created counter', async () => { + const tasks = [1000, 2000, 500, 300, 400, 15000, 20000, 200]; + const events$ = new Subject(); + const taskPollingLifecycle = taskPollingLifecycleMock.create({ + events$: events$ as Observable, + }); + const adHocTaskCounter = new AdHocTaskCounter(); + + const BackgroundTaskUtilizationAggregator = createBackgroundTaskUtilizationAggregator( + taskPollingLifecycle, + adHocTaskCounter, + pollInterval + ); + + function expectWindowEqualsUpdate( + taskStat: AggregatedStat, + window: number[] + ) { + expect(taskStat.value.adhoc.created.counter).toEqual(sum(window)); } - }); - }); - test('returns a running count of adhoc task_counter', async () => { - const tasks = [0, 0, 0, 0, 0, 0, 0, 0]; - const events$ = new Subject(); - const taskPollingLifecycle = taskPollingLifecycleMock.create({ - events$: events$ as Observable, - }); - const adHocTaskCounter = new AdHocTaskCounter(); - - const runningAverageWindowSize = 5; - const BackgroundTaskUtilizationAggregator = createBackgroundTaskUtilizationAggregator( - taskPollingLifecycle, - runningAverageWindowSize, - adHocTaskCounter, - pollInterval - ); - - function expectWindowEqualsUpdate( - taskStat: AggregatedStat, - window: number[] - ) { - expect(taskStat.value.adhoc.ran.service_time.task_counter).toEqual(window.length); - } - - return new Promise((resolve) => { - BackgroundTaskUtilizationAggregator.pipe( - // skip initial stat which is just initialized data which - // ensures we don't stall on combineLatest - skip(1), - // Use 'summarizeUtilizationStat' to receive summarize stats - map(({ key, value }: AggregatedStat) => ({ - key, - value, - })), - take(tasks.length), - bufferCount(tasks.length) - ).subscribe((taskStats: Array>) => { - expectWindowEqualsUpdate(taskStats[0], tasks.slice(0, 1)); - expectWindowEqualsUpdate(taskStats[1], tasks.slice(0, 2)); - expectWindowEqualsUpdate(taskStats[2], tasks.slice(0, 3)); - expectWindowEqualsUpdate(taskStats[3], tasks.slice(0, 4)); - expectWindowEqualsUpdate(taskStats[4], tasks.slice(0, 5)); - // from the 6th value, begin to drop old values as out window is 5 - expectWindowEqualsUpdate(taskStats[5], tasks.slice(0, 6)); - expectWindowEqualsUpdate(taskStats[6], tasks.slice(0, 7)); - expectWindowEqualsUpdate(taskStats[7], tasks.slice(0, 8)); - resolve(); + return new Promise((resolve) => { + BackgroundTaskUtilizationAggregator.pipe( + // skip initial stat which is just initialized data which + // ensures we don't stall on combineLatest + skip(1), + // Use 'summarizeUtilizationStat' to receive summarize stats + map(({ key, value }: AggregatedStat) => ({ + key, + value, + })), + take(tasks.length), + bufferCount(tasks.length) + ).subscribe((taskStats: Array>) => { + expectWindowEqualsUpdate(taskStats[0], tasks.slice(0, 1)); + expectWindowEqualsUpdate(taskStats[1], tasks.slice(0, 2)); + expectWindowEqualsUpdate(taskStats[2], tasks.slice(0, 3)); + expectWindowEqualsUpdate(taskStats[3], tasks.slice(0, 4)); + expectWindowEqualsUpdate(taskStats[4], tasks.slice(0, 5)); + // from the 6th value, begin to drop old values as out window is 5 + expectWindowEqualsUpdate(taskStats[5], tasks.slice(0, 6)); + expectWindowEqualsUpdate(taskStats[6], tasks.slice(0, 7)); + expectWindowEqualsUpdate(taskStats[7], tasks.slice(0, 8)); + resolve(); + }); + + for (const task of tasks) { + adHocTaskCounter.increment(task); + events$.next(mockTaskRunEvent({}, { start: 0, stop: 0 })); + } }); + }); - for (const task of tasks) { - events$.next(mockTaskRunEvent({}, { start: task, stop: task })); + test('returns a running count of recurring actual service_time', async () => { + const serviceTimes = [1000, 2000, 500, 300, 400, 15000, 20000, 200]; + const events$ = new Subject(); + const taskPollingLifecycle = taskPollingLifecycleMock.create({ + events$: events$ as Observable, + }); + const adHocTaskCounter = new AdHocTaskCounter(); + + const BackgroundTaskUtilizationAggregator = createBackgroundTaskUtilizationAggregator( + taskPollingLifecycle, + adHocTaskCounter, + pollInterval + ); + + function expectWindowEqualsUpdate( + taskStat: AggregatedStat, + window: number[] + ) { + expect(taskStat.value.recurring.ran.service_time.actual).toEqual(sum(window)); } - }); - }); - test('returns a running count of adhoc created counter', async () => { - const tasks = [1000, 2000, 500, 300, 400, 15000, 20000, 200]; - const events$ = new Subject(); - const taskPollingLifecycle = taskPollingLifecycleMock.create({ - events$: events$ as Observable, - }); - const adHocTaskCounter = new AdHocTaskCounter(); - - const runningAverageWindowSize = 5; - const BackgroundTaskUtilizationAggregator = createBackgroundTaskUtilizationAggregator( - taskPollingLifecycle, - runningAverageWindowSize, - adHocTaskCounter, - pollInterval - ); - - function expectWindowEqualsUpdate( - taskStat: AggregatedStat, - window: number[] - ) { - expect(taskStat.value.adhoc.created.counter).toEqual(sum(window)); - } - - return new Promise((resolve) => { - BackgroundTaskUtilizationAggregator.pipe( - // skip initial stat which is just initialized data which - // ensures we don't stall on combineLatest - skip(1), - // Use 'summarizeUtilizationStat' to receive summarize stats - map(({ key, value }: AggregatedStat) => ({ - key, - value, - })), - take(tasks.length), - bufferCount(tasks.length) - ).subscribe((taskStats: Array>) => { - expectWindowEqualsUpdate(taskStats[0], tasks.slice(0, 1)); - expectWindowEqualsUpdate(taskStats[1], tasks.slice(0, 2)); - expectWindowEqualsUpdate(taskStats[2], tasks.slice(0, 3)); - expectWindowEqualsUpdate(taskStats[3], tasks.slice(0, 4)); - expectWindowEqualsUpdate(taskStats[4], tasks.slice(0, 5)); - // from the 6th value, begin to drop old values as out window is 5 - expectWindowEqualsUpdate(taskStats[5], tasks.slice(0, 6)); - expectWindowEqualsUpdate(taskStats[6], tasks.slice(0, 7)); - expectWindowEqualsUpdate(taskStats[7], tasks.slice(0, 8)); - resolve(); + return new Promise((resolve) => { + const events = []; + const now = Date.now(); + for (const time of serviceTimes) { + events.push({ start: runAtMillisecondsAgo(now, time).getTime(), stop: now }); + } + BackgroundTaskUtilizationAggregator.pipe( + // skip initial stat which is just initialized data which + // ensures we don't stall on combineLatest + skip(1), + // Use 'summarizeUtilizationStat' to receive summarize stats + map(({ key, value }: AggregatedStat) => ({ + key, + value, + })), + take(serviceTimes.length), + bufferCount(serviceTimes.length) + ).subscribe((taskStats: Array>) => { + expectWindowEqualsUpdate(taskStats[0], serviceTimes.slice(0, 1)); + expectWindowEqualsUpdate(taskStats[1], serviceTimes.slice(0, 2)); + expectWindowEqualsUpdate(taskStats[2], serviceTimes.slice(0, 3)); + expectWindowEqualsUpdate(taskStats[3], serviceTimes.slice(0, 4)); + expectWindowEqualsUpdate(taskStats[4], serviceTimes.slice(0, 5)); + // from the 6th value, begin to drop old values as out window is 5 + expectWindowEqualsUpdate(taskStats[5], serviceTimes.slice(0, 6)); + expectWindowEqualsUpdate(taskStats[6], serviceTimes.slice(0, 7)); + expectWindowEqualsUpdate(taskStats[7], serviceTimes.slice(0, 8)); + resolve(); + }); + + for (const event of events) { + events$.next(mockTaskRunEvent({ schedule: { interval: '1h' } }, event)); + } }); + }); - for (const task of tasks) { - adHocTaskCounter.increment(task); - events$.next(mockTaskRunEvent({}, { start: 0, stop: 0 })); + test('returns a running count of recurring adjusted service_time', async () => { + const serviceTimes = [1000, 2000, 500, 300, 400, 15000, 20000, 200]; + const events$ = new Subject(); + const taskPollingLifecycle = taskPollingLifecycleMock.create({ + events$: events$ as Observable, + }); + const adHocTaskCounter = new AdHocTaskCounter(); + + const BackgroundTaskUtilizationAggregator = createBackgroundTaskUtilizationAggregator( + taskPollingLifecycle, + adHocTaskCounter, + pollInterval + ); + + function expectWindowEqualsUpdate( + taskStat: AggregatedStat, + window: number[] + ) { + expect(taskStat.value.recurring.ran.service_time.adjusted).toEqual(sum(window)); } - }); - }); - test('returns a running count of recurring actual service_time', async () => { - const serviceTimes = [1000, 2000, 500, 300, 400, 15000, 20000, 200]; - const events$ = new Subject(); - const taskPollingLifecycle = taskPollingLifecycleMock.create({ - events$: events$ as Observable, + return new Promise((resolve) => { + const events = []; + const now = Date.now(); + for (const time of serviceTimes) { + events.push({ start: runAtMillisecondsAgo(now, time).getTime(), stop: now }); + } + BackgroundTaskUtilizationAggregator.pipe( + // skip initial stat which is just initialized data which + // ensures we don't stall on combineLatest + skip(1), + // Use 'summarizeUtilizationStat' to receive summarize stats + map(({ key, value }: AggregatedStat) => ({ + key, + value, + })), + take(serviceTimes.length), + bufferCount(serviceTimes.length) + ).subscribe((taskStats: Array>) => { + expectWindowEqualsUpdate(taskStats[0], roundUpToNearestSec(serviceTimes.slice(0, 1), 3)); + expectWindowEqualsUpdate(taskStats[1], roundUpToNearestSec(serviceTimes.slice(0, 2), 3)); + expectWindowEqualsUpdate(taskStats[2], roundUpToNearestSec(serviceTimes.slice(0, 3), 3)); + expectWindowEqualsUpdate(taskStats[3], roundUpToNearestSec(serviceTimes.slice(0, 4), 3)); + expectWindowEqualsUpdate(taskStats[4], roundUpToNearestSec(serviceTimes.slice(0, 5), 3)); + // from the 6th value, begin to drop old values as out window is 5 + expectWindowEqualsUpdate(taskStats[5], roundUpToNearestSec(serviceTimes.slice(0, 6), 3)); + expectWindowEqualsUpdate(taskStats[6], roundUpToNearestSec(serviceTimes.slice(0, 7), 3)); + expectWindowEqualsUpdate(taskStats[7], roundUpToNearestSec(serviceTimes.slice(0, 8), 3)); + resolve(); + }); + + for (const event of events) { + events$.next(mockTaskRunEvent({ schedule: { interval: '1h' } }, event)); + } + }); }); - const adHocTaskCounter = new AdHocTaskCounter(); - - const runningAverageWindowSize = 5; - const BackgroundTaskUtilizationAggregator = createBackgroundTaskUtilizationAggregator( - taskPollingLifecycle, - runningAverageWindowSize, - adHocTaskCounter, - pollInterval - ); - - function expectWindowEqualsUpdate( - taskStat: AggregatedStat, - window: number[] - ) { - expect(taskStat.value.recurring.ran.service_time.actual).toEqual(sum(window)); - } - - return new Promise((resolve) => { - const events = []; - const now = Date.now(); - for (const time of serviceTimes) { - events.push({ start: runAtMillisecondsAgo(now, time).getTime(), stop: now }); + + test('returns a running count of recurring task_counter', async () => { + const tasks = [0, 0, 0, 0, 0, 0, 0, 0]; + const events$ = new Subject(); + const taskPollingLifecycle = taskPollingLifecycleMock.create({ + events$: events$ as Observable, + }); + const adHocTaskCounter = new AdHocTaskCounter(); + + const BackgroundTaskUtilizationAggregator = createBackgroundTaskUtilizationAggregator( + taskPollingLifecycle, + adHocTaskCounter, + pollInterval + ); + + function expectWindowEqualsUpdate( + taskStat: AggregatedStat, + window: number[] + ) { + expect(taskStat.value.recurring.ran.service_time.task_counter).toEqual(window.length); } - BackgroundTaskUtilizationAggregator.pipe( - // skip initial stat which is just initialized data which - // ensures we don't stall on combineLatest - skip(1), - // Use 'summarizeUtilizationStat' to receive summarize stats - map(({ key, value }: AggregatedStat) => ({ - key, - value, - })), - take(serviceTimes.length), - bufferCount(serviceTimes.length) - ).subscribe((taskStats: Array>) => { - expectWindowEqualsUpdate(taskStats[0], serviceTimes.slice(0, 1)); - expectWindowEqualsUpdate(taskStats[1], serviceTimes.slice(0, 2)); - expectWindowEqualsUpdate(taskStats[2], serviceTimes.slice(0, 3)); - expectWindowEqualsUpdate(taskStats[3], serviceTimes.slice(0, 4)); - expectWindowEqualsUpdate(taskStats[4], serviceTimes.slice(0, 5)); - // from the 6th value, begin to drop old values as out window is 5 - expectWindowEqualsUpdate(taskStats[5], serviceTimes.slice(0, 6)); - expectWindowEqualsUpdate(taskStats[6], serviceTimes.slice(0, 7)); - expectWindowEqualsUpdate(taskStats[7], serviceTimes.slice(0, 8)); - resolve(); + + return new Promise((resolve) => { + BackgroundTaskUtilizationAggregator.pipe( + // skip initial stat which is just initialized data which + // ensures we don't stall on combineLatest + skip(1), + // Use 'summarizeUtilizationStat' to receive summarize stats + map(({ key, value }: AggregatedStat) => ({ + key, + value, + })), + take(tasks.length), + bufferCount(tasks.length) + ).subscribe((taskStats: Array>) => { + expectWindowEqualsUpdate(taskStats[0], tasks.slice(0, 1)); + expectWindowEqualsUpdate(taskStats[1], tasks.slice(0, 2)); + expectWindowEqualsUpdate(taskStats[2], tasks.slice(0, 3)); + expectWindowEqualsUpdate(taskStats[3], tasks.slice(0, 4)); + expectWindowEqualsUpdate(taskStats[4], tasks.slice(0, 5)); + // from the 6th value, begin to drop old values as out window is 5 + expectWindowEqualsUpdate(taskStats[5], tasks.slice(0, 6)); + expectWindowEqualsUpdate(taskStats[6], tasks.slice(0, 7)); + expectWindowEqualsUpdate(taskStats[7], tasks.slice(0, 8)); + resolve(); + }); + + for (const task of tasks) { + events$.next( + mockTaskRunEvent({ schedule: { interval: '1h' } }, { start: task, stop: task }) + ); + } }); + }); - for (const event of events) { - events$.next(mockTaskRunEvent({ schedule: { interval: '1h' } }, event)); + test('returns a running count of load', async () => { + const loads = [40, 80, 100, 100, 10, 10, 60, 40]; + const events$ = new Subject(); + const taskPollingLifecycle = taskPollingLifecycleMock.create({ + events$: events$ as Observable, + }); + + const BackgroundTaskUtilizationAggregator = createBackgroundTaskUtilizationAggregator( + taskPollingLifecycle, + new AdHocTaskCounter(), + pollInterval + ); + + function expectWindowEqualsUpdate( + taskStat: AggregatedStat, + window: number[] + ) { + expect(taskStat.value.load).toEqual(mean(window)); } - }); - }); - test('returns a running count of recurring adjusted service_time', async () => { - const serviceTimes = [1000, 2000, 500, 300, 400, 15000, 20000, 200]; - const events$ = new Subject(); - const taskPollingLifecycle = taskPollingLifecycleMock.create({ - events$: events$ as Observable, + return new Promise((resolve) => { + BackgroundTaskUtilizationAggregator.pipe( + // skip initial stat which is just initialized data which + // ensures we don't stall on combineLatest + skip(1), + map(({ key, value }: AggregatedStat) => ({ + key, + value, + })), + take(loads.length), + bufferCount(loads.length) + ).subscribe((taskStats: Array>) => { + expectWindowEqualsUpdate(taskStats[0], loads.slice(0, 1)); + expectWindowEqualsUpdate(taskStats[1], loads.slice(0, 2)); + expectWindowEqualsUpdate(taskStats[2], loads.slice(0, 3)); + expectWindowEqualsUpdate(taskStats[3], loads.slice(0, 4)); + expectWindowEqualsUpdate(taskStats[4], loads.slice(0, 5)); + // from the 6th value, begin to drop old values as our window is 5 + expectWindowEqualsUpdate(taskStats[5], loads.slice(1, 6)); + expectWindowEqualsUpdate(taskStats[6], loads.slice(2, 7)); + expectWindowEqualsUpdate(taskStats[7], loads.slice(3, 8)); + resolve(); + }); + + for (const load of loads) { + events$.next(mockTaskStatEvent('workerUtilization', load)); + } + }); }); - const adHocTaskCounter = new AdHocTaskCounter(); - - const runningAverageWindowSize = 5; - const BackgroundTaskUtilizationAggregator = createBackgroundTaskUtilizationAggregator( - taskPollingLifecycle, - runningAverageWindowSize, - adHocTaskCounter, - pollInterval - ); - - function expectWindowEqualsUpdate( - taskStat: AggregatedStat, - window: number[] - ) { - expect(taskStat.value.recurring.ran.service_time.adjusted).toEqual(sum(window)); - } - - return new Promise((resolve) => { - const events = []; - const now = Date.now(); - for (const time of serviceTimes) { - events.push({ start: runAtMillisecondsAgo(now, time).getTime(), stop: now }); - } - BackgroundTaskUtilizationAggregator.pipe( - // skip initial stat which is just initialized data which - // ensures we don't stall on combineLatest - skip(1), - // Use 'summarizeUtilizationStat' to receive summarize stats - map(({ key, value }: AggregatedStat) => ({ - key, - value, - })), - take(serviceTimes.length), - bufferCount(serviceTimes.length) - ).subscribe((taskStats: Array>) => { - expectWindowEqualsUpdate(taskStats[0], roundUpToNearestSec(serviceTimes.slice(0, 1), 3)); - expectWindowEqualsUpdate(taskStats[1], roundUpToNearestSec(serviceTimes.slice(0, 2), 3)); - expectWindowEqualsUpdate(taskStats[2], roundUpToNearestSec(serviceTimes.slice(0, 3), 3)); - expectWindowEqualsUpdate(taskStats[3], roundUpToNearestSec(serviceTimes.slice(0, 4), 3)); - expectWindowEqualsUpdate(taskStats[4], roundUpToNearestSec(serviceTimes.slice(0, 5), 3)); - // from the 6th value, begin to drop old values as out window is 5 - expectWindowEqualsUpdate(taskStats[5], roundUpToNearestSec(serviceTimes.slice(0, 6), 3)); - expectWindowEqualsUpdate(taskStats[6], roundUpToNearestSec(serviceTimes.slice(0, 7), 3)); - expectWindowEqualsUpdate(taskStats[7], roundUpToNearestSec(serviceTimes.slice(0, 8), 3)); - resolve(); + + test('returns a running count of load with custom window size', async () => { + const loads = [40, 80, 100, 100, 10, 10, 60, 40]; + const events$ = new Subject(); + const taskPollingLifecycle = taskPollingLifecycleMock.create({ + events$: events$ as Observable, }); - for (const event of events) { - events$.next(mockTaskRunEvent({ schedule: { interval: '1h' } }, event)); + const BackgroundTaskUtilizationAggregator = createBackgroundTaskUtilizationAggregator( + taskPollingLifecycle, + new AdHocTaskCounter(), + pollInterval, + 3 + ); + + function expectWindowEqualsUpdate( + taskStat: AggregatedStat, + window: number[] + ) { + expect(taskStat.value.load).toEqual(mean(window)); } + + return new Promise((resolve) => { + BackgroundTaskUtilizationAggregator.pipe( + // skip initial stat which is just initialized data which + // ensures we don't stall on combineLatest + skip(1), + map(({ key, value }: AggregatedStat) => ({ + key, + value, + })), + take(loads.length), + bufferCount(loads.length) + ).subscribe((taskStats: Array>) => { + expectWindowEqualsUpdate(taskStats[0], loads.slice(0, 1)); + expectWindowEqualsUpdate(taskStats[1], loads.slice(0, 2)); + expectWindowEqualsUpdate(taskStats[2], loads.slice(0, 3)); + // from the 4th value, begin to drop old values as our window is 3 + expectWindowEqualsUpdate(taskStats[3], loads.slice(1, 4)); + expectWindowEqualsUpdate(taskStats[4], loads.slice(2, 5)); + expectWindowEqualsUpdate(taskStats[5], loads.slice(3, 6)); + expectWindowEqualsUpdate(taskStats[6], loads.slice(4, 7)); + expectWindowEqualsUpdate(taskStats[7], loads.slice(5, 8)); + resolve(); + }); + + for (const load of loads) { + events$.next(mockTaskStatEvent('workerUtilization', load)); + } + }); }); }); - test('returns a running count of recurring task_counter', async () => { - const tasks = [0, 0, 0, 0, 0, 0, 0, 0]; - const events$ = new Subject(); - const taskPollingLifecycle = taskPollingLifecycleMock.create({ - events$: events$ as Observable, + describe('summarizeUtilizationStats', () => { + const lastUpdate = '2023-04-02T17:34:41.371Z'; + const monitoredStats = { + timestamp: '2023-04-01T17:34:41.371Z', + value: { + adhoc: { + created: { + counter: 1, + }, + ran: { + service_time: { + actual: 10, + adjusted: 7, + task_counter: 3, + }, + }, + }, + recurring: { + ran: { + service_time: { + actual: 79, + adjusted: 66, + task_counter: 10, + }, + }, + }, + load: 63, + }, + }; + + test('should return null if monitoredStats is null', () => { + expect( + summarizeUtilizationStats({ + lastUpdate, + // @ts-expect-error + monitoredStats: null, + isInternal: false, + }) + ).toEqual({ + last_update: lastUpdate, + stats: null, + }); }); - const adHocTaskCounter = new AdHocTaskCounter(); - - const runningAverageWindowSize = 5; - const BackgroundTaskUtilizationAggregator = createBackgroundTaskUtilizationAggregator( - taskPollingLifecycle, - runningAverageWindowSize, - adHocTaskCounter, - pollInterval - ); - - function expectWindowEqualsUpdate( - taskStat: AggregatedStat, - window: number[] - ) { - expect(taskStat.value.recurring.ran.service_time.task_counter).toEqual(window.length); - } - - return new Promise((resolve) => { - BackgroundTaskUtilizationAggregator.pipe( - // skip initial stat which is just initialized data which - // ensures we don't stall on combineLatest - skip(1), - // Use 'summarizeUtilizationStat' to receive summarize stats - map(({ key, value }: AggregatedStat) => ({ - key, - value, - })), - take(tasks.length), - bufferCount(tasks.length) - ).subscribe((taskStats: Array>) => { - expectWindowEqualsUpdate(taskStats[0], tasks.slice(0, 1)); - expectWindowEqualsUpdate(taskStats[1], tasks.slice(0, 2)); - expectWindowEqualsUpdate(taskStats[2], tasks.slice(0, 3)); - expectWindowEqualsUpdate(taskStats[3], tasks.slice(0, 4)); - expectWindowEqualsUpdate(taskStats[4], tasks.slice(0, 5)); - // from the 6th value, begin to drop old values as out window is 5 - expectWindowEqualsUpdate(taskStats[5], tasks.slice(0, 6)); - expectWindowEqualsUpdate(taskStats[6], tasks.slice(0, 7)); - expectWindowEqualsUpdate(taskStats[7], tasks.slice(0, 8)); - resolve(); + + test('should return null if monitoredStats value is not defined', () => { + expect( + summarizeUtilizationStats({ + lastUpdate, + // @ts-expect-error + monitoredStats: {}, + isInternal: false, + }) + ).toEqual({ + last_update: lastUpdate, + stats: null, }); + }); - for (const task of tasks) { - events$.next( - mockTaskRunEvent({ schedule: { interval: '1h' } }, { start: task, stop: task }) - ); - } + test('should return summary with all stats when isInternal is true', () => { + expect( + summarizeUtilizationStats({ + lastUpdate, + monitoredStats, + isInternal: true, + }) + ).toEqual({ + last_update: lastUpdate, + stats: { + timestamp: monitoredStats.timestamp, + value: monitoredStats.value, + }, + }); + }); + + test('should return summary with only public stats when isInternal is false', () => { + expect( + summarizeUtilizationStats({ + lastUpdate, + monitoredStats, + isInternal: false, + }) + ).toEqual({ + last_update: lastUpdate, + stats: { + timestamp: monitoredStats.timestamp, + value: { + load: 63, + }, + }, + }); }); }); }); @@ -440,6 +629,10 @@ function roundUpToNearestSec(duration: number[], s: number): number[] { return duration.map((d) => Math.ceil(d / pollInterval) * pollInterval); } +const mockTaskStatEvent = (type: TaskManagerStats, value: number) => { + return asTaskManagerStatEvent(type, asOk(value)); +}; + const mockTaskRunEvent = ( overrides: Partial = {}, timing: TaskTiming, diff --git a/x-pack/plugins/task_manager/server/monitoring/background_task_utilization_statistics.ts b/x-pack/plugins/task_manager/server/monitoring/background_task_utilization_statistics.ts index 761ad4252ac83..fd116cbdd71d8 100644 --- a/x-pack/plugins/task_manager/server/monitoring/background_task_utilization_statistics.ts +++ b/x-pack/plugins/task_manager/server/monitoring/background_task_utilization_statistics.ts @@ -6,17 +6,29 @@ */ import { JsonObject } from '@kbn/utility-types'; -import { get } from 'lodash'; +import { get, pick } from 'lodash'; +import stats from 'stats-lite'; import { combineLatest, filter, map, Observable, startWith } from 'rxjs'; import { AdHocTaskCounter } from '../lib/adhoc_task_counter'; -import { unwrap } from '../lib/result_type'; +import { mapOk, unwrap } from '../lib/result_type'; import { TaskLifecycleEvent, TaskPollingLifecycle } from '../polling_lifecycle'; import { ConcreteTaskInstance } from '../task'; -import { isTaskRunEvent, TaskRun, TaskTiming } from '../task_events'; +import { + isTaskManagerWorkerUtilizationStatEvent, + isTaskRunEvent, + TaskRun, + TaskTiming, +} from '../task_events'; import { MonitoredStat } from './monitoring_stats_stream'; import { AggregatedStat, AggregatedStatProvider } from './runtime_statistics_aggregator'; +import { createRunningAveragedStat } from './task_run_calcultors'; +import { DEFAULT_WORKER_UTILIZATION_RUNNING_AVERAGE_WINDOW } from '../config'; -export interface BackgroundTaskUtilizationStat extends JsonObject { +export interface PublicBackgroundTaskUtilizationStat extends JsonObject { + load: number; +} + +export interface BackgroundTaskUtilizationStat extends PublicBackgroundTaskUtilizationStat { adhoc: AdhocTaskStat; recurring: TaskStat; } @@ -39,11 +51,11 @@ interface AdhocTaskStat extends TaskStat { export function createBackgroundTaskUtilizationAggregator( taskPollingLifecycle: TaskPollingLifecycle, - runningAverageWindowSize: number, adHocTaskCounter: AdHocTaskCounter, - pollInterval: number + pollInterval: number, + workerUtilizationRunningAverageWindowSize: number = DEFAULT_WORKER_UTILIZATION_RUNNING_AVERAGE_WINDOW ): AggregatedStatProvider { - const taskRunEventToAdhocStat = createTaskRunEventToAdhocStat(runningAverageWindowSize); + const taskRunEventToAdhocStat = createTaskRunEventToAdhocStat(); const taskRunAdhocEvents$: Observable> = taskPollingLifecycle.events.pipe( filter((taskEvent: TaskLifecycleEvent) => isTaskRunEvent(taskEvent) && hasTiming(taskEvent)), @@ -57,7 +69,7 @@ export function createBackgroundTaskUtilizationAggregator( }) ); - const taskRunEventToRecurringStat = createTaskRunEventToRecurringStat(runningAverageWindowSize); + const taskRunEventToRecurringStat = createTaskRunEventToRecurringStat(); const taskRunRecurringEvents$: Observable> = taskPollingLifecycle.events.pipe( filter((taskEvent: TaskLifecycleEvent) => isTaskRunEvent(taskEvent) && hasTiming(taskEvent)), @@ -71,6 +83,18 @@ export function createBackgroundTaskUtilizationAggregator( }) ); + const taskManagerUtilizationEventToLoadStat = createTaskRunEventToLoadStat( + workerUtilizationRunningAverageWindowSize + ); + + const taskManagerWorkerUtilizationEvent$: Observable< + Pick + > = taskPollingLifecycle.events.pipe( + filter(isTaskManagerWorkerUtilizationStatEvent), + map((taskEvent: TaskLifecycleEvent) => taskEvent.event), + map(mapOk((num: number) => taskManagerUtilizationEventToLoadStat(num))) + ); + return combineLatest([ taskRunAdhocEvents$.pipe( startWith({ @@ -101,17 +125,24 @@ export function createBackgroundTaskUtilizationAggregator( }, }) ), + taskManagerWorkerUtilizationEvent$.pipe( + startWith({ + load: 0, + }) + ), ]).pipe( map( - ([adhoc, recurring]: [ + ([adhoc, recurring, load]: [ Pick, - Pick + Pick, + Pick ]) => { return { key: 'utilization', value: { ...adhoc, ...recurring, + ...load, }, } as AggregatedStat; } @@ -123,31 +154,41 @@ function hasTiming(taskEvent: TaskLifecycleEvent) { return !!taskEvent?.timing; } -export function summarizeUtilizationStats({ - // eslint-disable-next-line @typescript-eslint/naming-convention - last_update, - stats, -}: { - last_update: string; - stats: MonitoredStat | undefined; -}): { +interface SummarizeUtilizationStatsOpts { + lastUpdate: string; + monitoredStats: MonitoredStat | undefined; + isInternal: boolean; +} + +interface SummarizeUtilizationStatsResult { last_update: string; - stats: MonitoredStat | null; -} { - const utilizationStats = stats?.value; + stats: + | MonitoredStat + | MonitoredStat + | null; +} + +export function summarizeUtilizationStats({ + lastUpdate, + monitoredStats, + isInternal, +}: SummarizeUtilizationStatsOpts): SummarizeUtilizationStatsResult { + const utilizationStats = monitoredStats?.value; + + if (!monitoredStats || !utilizationStats) { + return { last_update: lastUpdate, stats: null }; + } + return { - last_update, - stats: - stats && utilizationStats - ? { - timestamp: stats.timestamp, - value: utilizationStats, - } - : null, + last_update: lastUpdate, + stats: { + timestamp: monitoredStats.timestamp, + value: isInternal ? utilizationStats : pick(utilizationStats, 'load'), + }, }; } -function createTaskRunEventToAdhocStat(runningAverageWindowSize: number) { +function createTaskRunEventToAdhocStat() { let createdCounter = 0; let actualCounter = 0; let adjustedCounter = 0; @@ -177,7 +218,7 @@ function createTaskRunEventToAdhocStat(runningAverageWindowSize: number) { }; } -function createTaskRunEventToRecurringStat(runningAverageWindowSize: number) { +function createTaskRunEventToRecurringStat() { let actualCounter = 0; let adjustedCounter = 0; let taskCounter = 0; @@ -201,6 +242,16 @@ function createTaskRunEventToRecurringStat(runningAverageWindowSize: number) { }; } +function createTaskRunEventToLoadStat(workerUtilizationRunningAverageWindowSize: number) { + const loadQueue = createRunningAveragedStat(workerUtilizationRunningAverageWindowSize); + return (load: number): Pick => { + const historicalLoad = loadQueue(load); + return { + load: stats.mean(historicalLoad), + }; + }; +} + function getServiceTimeStats(timing: TaskTiming, pollInterval: number) { const duration = timing!.stop - timing!.start; const adjusted = Math.ceil(duration / pollInterval) * pollInterval; diff --git a/x-pack/plugins/task_manager/server/monitoring/configuration_statistics.test.ts b/x-pack/plugins/task_manager/server/monitoring/configuration_statistics.test.ts index 764b7aa15335b..b63c52893c5d8 100644 --- a/x-pack/plugins/task_manager/server/monitoring/configuration_statistics.test.ts +++ b/x-pack/plugins/task_manager/server/monitoring/configuration_statistics.test.ts @@ -45,6 +45,7 @@ describe('Configuration Statistics Aggregator', () => { monitor: true, warn_threshold: 5000, }, + worker_utilization_running_average_window: 5, }; const managedConfig = { diff --git a/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.test.ts b/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.test.ts index 610e0bf080b05..8d6df288e702d 100644 --- a/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.test.ts +++ b/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.test.ts @@ -49,6 +49,7 @@ describe('createMonitoringStatsStream', () => { monitor: true, warn_threshold: 5000, }, + worker_utilization_running_average_window: 5, }; it('returns the initial config used to configure Task Manager', async () => { diff --git a/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.ts b/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.ts index e192a72f7092d..e1ff38d1c9607 100644 --- a/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.ts +++ b/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.ts @@ -108,9 +108,9 @@ export function createAggregators( createTaskRunAggregator(taskPollingLifecycle, config.monitored_stats_running_average_window), createBackgroundTaskUtilizationAggregator( taskPollingLifecycle, - config.monitored_stats_running_average_window, adHocTaskCounter, - config.poll_interval + config.poll_interval, + config.worker_utilization_running_average_window ) ); } diff --git a/x-pack/plugins/task_manager/server/plugin.test.ts b/x-pack/plugins/task_manager/server/plugin.test.ts index ad02e6d7490f5..fce648c87f6e9 100644 --- a/x-pack/plugins/task_manager/server/plugin.test.ts +++ b/x-pack/plugins/task_manager/server/plugin.test.ts @@ -70,6 +70,7 @@ const pluginInitializerContextParams = { monitor: true, warn_threshold: 5000, }, + worker_utilization_running_average_window: 5, }; describe('TaskManagerPlugin', () => { diff --git a/x-pack/plugins/task_manager/server/polling_lifecycle.test.ts b/x-pack/plugins/task_manager/server/polling_lifecycle.test.ts index 51cac494d3a67..9e62eec402b2d 100644 --- a/x-pack/plugins/task_manager/server/polling_lifecycle.test.ts +++ b/x-pack/plugins/task_manager/server/polling_lifecycle.test.ts @@ -75,6 +75,7 @@ describe('TaskPollingLifecycle', () => { monitor: true, warn_threshold: 5000, }, + worker_utilization_running_average_window: 5, }, taskStore: mockTaskStore, logger: taskManagerLogger, diff --git a/x-pack/plugins/task_manager/server/polling_lifecycle.ts b/x-pack/plugins/task_manager/server/polling_lifecycle.ts index ce5a142dc433e..e9b9eaca1e5d3 100644 --- a/x-pack/plugins/task_manager/server/polling_lifecycle.ts +++ b/x-pack/plugins/task_manager/server/polling_lifecycle.ts @@ -176,8 +176,13 @@ export class TaskPollingLifecycle { const capacity = this.pool.availableWorkers; if (!capacity) { // if there isn't capacity, emit a load event so that we can expose how often - // high load causes the poller to skip work (work isn'tcalled when there is no capacity) + // high load causes the poller to skip work (work isn't called when there is no capacity) this.emitEvent(asTaskManagerStatEvent('load', asOk(this.pool.workerLoad))); + + // Emit event indicating task manager utilization + this.emitEvent( + asTaskManagerStatEvent('workerUtilization', asOk(this.pool.workerLoad)) + ); } return capacity; }, @@ -187,7 +192,16 @@ export class TaskPollingLifecycle { // (such as polling for new work, marking tasks as running etc.) but does not // include the time of actually running the task workTimeout: pollInterval * maxPollInactivityCycles, - }), + }).pipe( + tap( + mapOk(() => { + // Emit event indicating task manager utilization % at the end of a polling cycle + this.emitEvent( + asTaskManagerStatEvent('workerUtilization', asOk(this.pool.workerLoad)) + ); + }) + ) + ), { heartbeatInterval: pollInterval, // Time out the poller itself if it has failed to complete the entire stream for a certain amount of time. diff --git a/x-pack/plugins/task_manager/server/routes/background_task_utilization.test.ts b/x-pack/plugins/task_manager/server/routes/background_task_utilization.test.ts index 66052b095a258..1ccc0d89f804a 100644 --- a/x-pack/plugins/task_manager/server/routes/background_task_utilization.test.ts +++ b/x-pack/plugins/task_manager/server/routes/background_task_utilization.test.ts @@ -37,7 +37,7 @@ describe('backgroundTaskUtilizationRoute', () => { jest.resetAllMocks(); }); - it('registers the route', async () => { + it('registers internal and public route', async () => { const router = httpServiceMock.createRouter(); backgroundTaskUtilizationRoute({ router, @@ -51,11 +51,15 @@ describe('backgroundTaskUtilizationRoute', () => { usageCounter: mockUsageCounter, }); - const [config] = router.get.mock.calls[0]; + const [config1] = router.get.mock.calls[0]; - expect(config.path).toMatchInlineSnapshot( + expect(config1.path).toMatchInlineSnapshot( `"/internal/task_manager/_background_task_utilization"` ); + + const [config2] = router.get.mock.calls[1]; + + expect(config2.path).toMatchInlineSnapshot(`"/api/task_manager/_background_task_utilization"`); }); it('checks user privileges and increments usage counter when API is accessed', async () => { diff --git a/x-pack/plugins/task_manager/server/routes/background_task_utilization.ts b/x-pack/plugins/task_manager/server/routes/background_task_utilization.ts index a1e0f5c4281f9..0d7eabee2a7ce 100644 --- a/x-pack/plugins/task_manager/server/routes/background_task_utilization.ts +++ b/x-pack/plugins/task_manager/server/routes/background_task_utilization.ts @@ -21,6 +21,7 @@ import { MonitoringStats } from '../monitoring'; import { TaskManagerConfig } from '../config'; import { BackgroundTaskUtilizationStat, + PublicBackgroundTaskUtilizationStat, summarizeUtilizationStats, } from '../monitoring/background_task_utilization_statistics'; import { MonitoredStat } from '../monitoring/monitoring_stats_stream'; @@ -29,7 +30,10 @@ export interface MonitoredUtilization { process_uuid: string; timestamp: string; last_update: string; - stats: MonitoredStat | null; + stats: + | MonitoredStat + | MonitoredStat + | null; } export interface BackgroundTaskUtilRouteParams { @@ -44,6 +48,12 @@ export interface BackgroundTaskUtilRouteParams { usageCounter?: UsageCounter; } +// Create an internal and public route so we can test out experimental metrics +const routeOptions = [ + { basePath: 'internal', isInternal: true }, + { basePath: 'api', isInternal: false }, +]; + export function backgroundTaskUtilizationRoute( params: BackgroundTaskUtilRouteParams ): Observable { @@ -61,10 +71,11 @@ export function backgroundTaskUtilizationRoute( const requiredHotStatsFreshness: number = config.monitored_stats_required_freshness; - function getBackgroundTaskUtilization(monitoredStats: MonitoringStats) { + function getBackgroundTaskUtilization(monitoredStats: MonitoringStats, isInternal: boolean) { const summarizedStats = summarizeUtilizationStats({ - last_update: monitoredStats.last_update, - stats: monitoredStats.stats.utilization, + lastUpdate: monitoredStats.last_update, + monitoredStats: monitoredStats.stats.utilization, + isInternal, }); const now = Date.now(); const timestamp = new Date(now).toISOString(); @@ -83,7 +94,7 @@ export function backgroundTaskUtilizationRoute( }), // Only calculate the summarized stats (calculates all running averages and evaluates state) // when needed by throttling down to the requiredHotStatsFreshness - map((stats) => getBackgroundTaskUtilization(stats)) + map((stats) => getBackgroundTaskUtilization(stats, true)) ) .subscribe((utilizationStats) => { monitoredUtilization$.next(utilizationStats); @@ -92,58 +103,60 @@ export function backgroundTaskUtilizationRoute( } }); - router.get( - { - path: '/internal/task_manager/_background_task_utilization', - // Uncomment when we determine that we can restrict API usage to Global admins based on telemetry - // options: { tags: ['access:taskManager'] }, - validate: false, - }, - async function ( - context: RequestHandlerContext, - req: KibanaRequest, - res: KibanaResponseFactory - ): Promise { - // If we are able to count usage, we want to check whether the user has access to - // the `taskManager` feature, which is only available as part of the Global All privilege. - if (usageCounter) { - const clusterClient = await getClusterClient(); - const hasPrivilegesResponse = await clusterClient - .asScoped(req) - .asCurrentUser.security.hasPrivileges({ - body: { - application: [ - { - application: `kibana-${kibanaIndexName}`, - resources: ['*'], - privileges: [`api:${kibanaVersion}:taskManager`], - }, - ], - }, - }); + routeOptions.forEach((routeOption) => { + router.get( + { + path: `/${routeOption.basePath}/task_manager/_background_task_utilization`, + // Uncomment when we determine that we can restrict API usage to Global admins based on telemetry + // options: { tags: ['access:taskManager'] }, + validate: false, + }, + async function ( + _: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise { + // If we are able to count usage, we want to check whether the user has access to + // the `taskManager` feature, which is only available as part of the Global All privilege. + if (usageCounter) { + const clusterClient = await getClusterClient(); + const hasPrivilegesResponse = await clusterClient + .asScoped(req) + .asCurrentUser.security.hasPrivileges({ + body: { + application: [ + { + application: `kibana-${kibanaIndexName}`, + resources: ['*'], + privileges: [`api:${kibanaVersion}:taskManager`], + }, + ], + }, + }); - // Keep track of total access vs admin access - usageCounter.incrementCounter({ - counterName: `taskManagerBackgroundTaskUtilApiAccess`, - counterType: 'taskManagerBackgroundTaskUtilApi', - incrementBy: 1, - }); - if (hasPrivilegesResponse.has_all_requested) { + // Keep track of total access vs admin access usageCounter.incrementCounter({ - counterName: `taskManagerBackgroundTaskUtilApiAdminAccess`, + counterName: `taskManagerBackgroundTaskUtilApiAccess`, counterType: 'taskManagerBackgroundTaskUtilApi', incrementBy: 1, }); + if (hasPrivilegesResponse.has_all_requested) { + usageCounter.incrementCounter({ + counterName: `taskManagerBackgroundTaskUtilApiAdminAccess`, + counterType: 'taskManagerBackgroundTaskUtilApi', + incrementBy: 1, + }); + } } - } - return res.ok({ - body: lastMonitoredStats - ? getBackgroundTaskUtilization(lastMonitoredStats) - : { process_uuid: taskManagerId, timestamp: new Date().toISOString(), stats: {} }, - }); - } - ); + return res.ok({ + body: lastMonitoredStats + ? getBackgroundTaskUtilization(lastMonitoredStats, routeOption.isInternal) + : { process_uuid: taskManagerId, timestamp: new Date().toISOString(), stats: {} }, + }); + } + ); + }); return monitoredUtilization$; } diff --git a/x-pack/plugins/task_manager/server/task_events.ts b/x-pack/plugins/task_manager/server/task_events.ts index d0bcaf2724188..e8808322b397a 100644 --- a/x-pack/plugins/task_manager/server/task_events.ts +++ b/x-pack/plugins/task_manager/server/task_events.ts @@ -87,7 +87,8 @@ export type TaskManagerStats = | 'pollingDelay' | 'claimDuration' | 'queuedEphemeralTasks' - | 'ephemeralTaskDelay'; + | 'ephemeralTaskDelay' + | 'workerUtilization'; export type TaskManagerStat = TaskEvent; export type OkResultOf = EventType extends TaskEvent @@ -211,6 +212,11 @@ export function isTaskManagerStatEvent( ): taskEvent is TaskManagerStat { return taskEvent.type === TaskEventType.TASK_MANAGER_STAT; } +export function isTaskManagerWorkerUtilizationStatEvent( + taskEvent: TaskEvent +): taskEvent is TaskManagerStat { + return taskEvent.type === TaskEventType.TASK_MANAGER_STAT && taskEvent.id === 'workerUtilization'; +} export function isEphemeralTaskRejectedDueToCapacityEvent( taskEvent: TaskEvent ): taskEvent is EphemeralTaskRejectedDueToCapacity { diff --git a/x-pack/plugins/task_manager/server/usage/task_manager_usage_collector.test.ts b/x-pack/plugins/task_manager/server/usage/task_manager_usage_collector.test.ts index f41a0598ff50c..c8cb2787a90fb 100644 --- a/x-pack/plugins/task_manager/server/usage/task_manager_usage_collector.test.ts +++ b/x-pack/plugins/task_manager/server/usage/task_manager_usage_collector.test.ts @@ -19,6 +19,8 @@ import { registerTaskManagerUsageCollector } from './task_manager_usage_collecto import { sleep } from '../test_utils'; import { TaskManagerUsage } from './types'; import { MonitoredUtilization } from '../routes/background_task_utilization'; +import { MonitoredStat } from '../monitoring/monitoring_stats_stream'; +import { BackgroundTaskUtilizationStat } from '../monitoring/background_task_utilization_statistics'; describe('registerTaskManagerUsageCollector', () => { let collector: Collector; @@ -113,18 +115,20 @@ describe('registerTaskManagerUsageCollector', () => { const mockHealth = getMockMonitoredHealth(); monitoringStats$.next(mockHealth); const mockUtilization = getMockMonitoredUtilization(); + const mockUtilizationStats = + mockUtilization.stats as MonitoredStat; monitoringUtilization$.next(mockUtilization); await sleep(1001); expect(usageCollectionMock.makeUsageCollector).toBeCalled(); const telemetry: TaskManagerUsage = (await collector.fetch(fetchContext)) as TaskManagerUsage; expect(telemetry.recurring_tasks).toEqual({ - actual_service_time: mockUtilization.stats?.value.recurring.ran.service_time.actual, - adjusted_service_time: mockUtilization.stats?.value.recurring.ran.service_time.adjusted, + actual_service_time: mockUtilizationStats?.value.recurring.ran.service_time.actual, + adjusted_service_time: mockUtilizationStats?.value.recurring.ran.service_time.adjusted, }); expect(telemetry.adhoc_tasks).toEqual({ - actual_service_time: mockUtilization.stats?.value.adhoc.ran.service_time.actual, - adjusted_service_time: mockUtilization.stats?.value.adhoc.ran.service_time.adjusted, + actual_service_time: mockUtilizationStats?.value.adhoc.ran.service_time.actual, + adjusted_service_time: mockUtilizationStats?.value.adhoc.ran.service_time.adjusted, }); }); @@ -308,6 +312,7 @@ function getMockMonitoredUtilization(overrides = {}): MonitoredUtilization { stats: { timestamp: new Date().toISOString(), value: { + load: 6, adhoc: { created: { counter: 5, diff --git a/x-pack/plugins/task_manager/server/usage/task_manager_usage_collector.ts b/x-pack/plugins/task_manager/server/usage/task_manager_usage_collector.ts index a4bd3049a0b49..6c8809c5c3d98 100644 --- a/x-pack/plugins/task_manager/server/usage/task_manager_usage_collector.ts +++ b/x-pack/plugins/task_manager/server/usage/task_manager_usage_collector.ts @@ -9,6 +9,8 @@ import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; import { MonitoredHealth } from '../routes/health'; import { TaskManagerUsage } from './types'; import { MonitoredUtilization } from '../routes/background_task_utilization'; +import { BackgroundTaskUtilizationStat } from '../monitoring/background_task_utilization_statistics'; +import { MonitoredStat } from '../monitoring/monitoring_stats_stream'; export function createTaskManagerUsageCollector( usageCollection: UsageCollectionSetup, @@ -19,12 +21,13 @@ export function createTaskManagerUsageCollector( excludeTaskTypes: string[] ) { let lastMonitoredHealth: MonitoredHealth | null = null; - let lastMonitoredUtilization: MonitoredUtilization | null = null; + let lastMonitoredUtilizationStats: MonitoredStat | null = null; combineLatest([monitoringStats$, monitoredUtilization$]) .pipe() .subscribe(([health, utilization]) => { lastMonitoredHealth = health; - lastMonitoredUtilization = utilization; + lastMonitoredUtilizationStats = + (utilization?.stats as MonitoredStat) ?? null; }); return usageCollection.makeUsageCollector({ @@ -69,15 +72,15 @@ export function createTaskManagerUsageCollector( ), recurring_tasks: { actual_service_time: - lastMonitoredUtilization?.stats?.value.recurring.ran.service_time.actual ?? 0, + lastMonitoredUtilizationStats?.value.recurring.ran.service_time.actual ?? 0, adjusted_service_time: - lastMonitoredUtilization?.stats?.value.recurring.ran.service_time.adjusted ?? 0, + lastMonitoredUtilizationStats?.value.recurring.ran.service_time.adjusted ?? 0, }, adhoc_tasks: { actual_service_time: - lastMonitoredUtilization?.stats?.value.adhoc.ran.service_time.actual ?? 0, + lastMonitoredUtilizationStats?.value.adhoc.ran.service_time.actual ?? 0, adjusted_service_time: - lastMonitoredUtilization?.stats?.value.adhoc.ran.service_time.adjusted ?? 0, + lastMonitoredUtilizationStats?.value.adhoc.ran.service_time.adjusted ?? 0, }, capacity: lastMonitoredHealth?.stats.capacity_estimation?.value.observed diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 5c3e05fe9671e..eac17d48bc9bc 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -12159,11 +12159,9 @@ "xpack.enterpriseSearch.content.indices.configurationConnector.config.submitButton.title": "Enregistrer la configuration", "xpack.enterpriseSearch.content.indices.configurationConnector.config.warning.title": "Ce connecteur est lié à votre index Elastic", "xpack.enterpriseSearch.content.indices.configurationConnector.configuration.successToast.title": "Configuration mise à jour", - "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.button.label": "Explorer le référentiel de connecteurs", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.clientExamplesLink": "exemples de clients connecteurs", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.configurationFileLink": "fichier de configuration", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.connectorDeployedText": "Une fois que vous avez configuré le connecteur, déployez-le sur votre infrastructure autogérée.", - "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.description.firstParagraph": "Le référentiel de connecteurs contient plusieurs exemples de clients connecteurs pour vous aider à utiliser notre cadre de développement accéléré avec des sources de données personnalisées.", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.waitingForConnector.button.label": "Revérifier maintenant", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.waitingForConnectorText": "Votre connecteur ne s'est pas connecté à Enterprise Search. Résolvez vos problèmes de configuration et actualisez la page.", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.waitingForConnectorTitle": "En attente de votre connecteur", @@ -17877,7 +17875,6 @@ "xpack.infra.openView.columnNames.actions": "Actions", "xpack.infra.openView.columnNames.name": "Nom", "xpack.infra.openView.flyoutHeader": "Gérer les vues enregistrées", - "xpack.infra.openView.loadButton": "Charger la vue", "xpack.infra.registerFeatures.infraOpsDescription": "Explorez les indicateurs et logs d'infrastructure pour les serveurs, conteneurs et services courants.", "xpack.infra.registerFeatures.infraOpsTitle": "Indicateurs", "xpack.infra.registerFeatures.logsDescription": "Diffusez les logs en temps réel ou faites défiler les vues d'historique comme sur une console.", @@ -17887,10 +17884,8 @@ "xpack.infra.savedView.errorOnCreate.duplicateViewName": "Une vue portant ce nom existe déjà.", "xpack.infra.savedView.errorOnCreate.title": "Une erreur s'est produite lors de l'enregistrement de la vue.", "xpack.infra.savedView.findError.title": "Une erreur s'est produite lors du chargement des vues.", - "xpack.infra.savedView.loadView": "Charger la vue", "xpack.infra.savedView.manageViews": "Gérer les vues", "xpack.infra.savedView.saveNewView": "Enregistrer la nouvelle vue", - "xpack.infra.savedView.searchPlaceholder": "Rechercher les vues enregistrées", "xpack.infra.savedView.unknownView": "Aucune vue sélectionnée", "xpack.infra.savedView.updateView": "Mettre à jour la vue", "xpack.infra.showHistory": "Afficher l'historique", @@ -18003,7 +17998,6 @@ "xpack.infra.waffle.region": "Tous", "xpack.infra.waffle.regionLabel": "Région", "xpack.infra.waffle.savedView.createHeader": "Enregistrer la vue", - "xpack.infra.waffle.savedView.selectViewHeader": "Sélectionner une vue à charger", "xpack.infra.waffle.savedView.updateHeader": "Mettre à jour la vue", "xpack.infra.waffle.savedViews.cancel": "annuler", "xpack.infra.waffle.savedViews.cancelButton": "Annuler", @@ -34717,9 +34711,7 @@ "xpack.synthetics.monitorManagement.editMonitorCrumb": "Modifier le moniteur", "xpack.synthetics.monitorManagement.editMonitorError": "Erreur lors du chargement de la liste Gestion des moniteurs", "xpack.synthetics.monitorManagement.editMonitorError.description": "Les paramètres de Gestion des moniteurs n'ont pas pu être chargés. Veuillez contacter le support technique.", - "xpack.synthetics.monitorManagement.emptyState.enablement": "Activez la Gestion des moniteurs pour exécuter des moniteurs légers et basés sur un navigateur réel à partir d'emplacements de test hébergés dans le monde entier. L'activation de la Gestion des moniteurs générera une clé d'API pour autoriser le service Synthetics à mettre à jour votre cluster Elasticsearch.", "xpack.synthetics.monitorManagement.emptyState.enablement.doc": "Lisez les documents", - "xpack.synthetics.monitorManagement.emptyState.enablement.enabled.title": "Activer la Gestion des moniteurs", "xpack.synthetics.monitorManagement.emptyState.enablement.learnMore": "Envie d'en savoir plus ?", "xpack.synthetics.monitorManagement.emptyState.enablement.title": "Activer", "xpack.synthetics.monitorManagement.failed": "ÉCHOUÉ", @@ -34785,9 +34777,7 @@ "xpack.synthetics.monitorManagement.startAddingLocationsDescription": "Les emplacements privés vous permettent d'exploiter des moniteurs depuis vos propres locaux. Ils nécessitent un agent Elastic et une politique d'agent que vous pouvez contrôler et maintenir via Fleet.", "xpack.synthetics.monitorManagement.steps": "Étapes", "xpack.synthetics.monitorManagement.summary.heading": "Résumé", - "xpack.synthetics.monitorManagement.syntheticsDisabledSuccess": "Gestion des moniteurs désactivée avec succès.", "xpack.synthetics.monitorManagement.syntheticsEnableLabel.management": "Activer la Gestion des moniteurs", - "xpack.synthetics.monitorManagement.syntheticsEnableSuccess": "Gestion des moniteurs activée avec succès.", "xpack.synthetics.monitorManagement.testResult": "Résultat du test", "xpack.synthetics.monitorManagement.testResults": "Résultats de test", "xpack.synthetics.monitorManagement.testRuns.label": "Exécutions de test", @@ -35171,7 +35161,6 @@ "xpack.synthetics.testResults.expandedRow.response_body.notRecorded": "Corps non enregistré. Définissez l'option du corps de la réponse de l'index sur \"Toujours activé\" dans les options avancées de la configuration du moniteur pour enregistrer le corps.", "xpack.synthetics.testRun.description": "Tester votre moniteur et vérifier les résultats avant d'enregistrer", "xpack.synthetics.testRun.pushError": "Impossible d'envoyer le moniteur vers le service.", - "xpack.synthetics.testRun.pushErrorLabel": "Erreur d'envoi", "xpack.synthetics.testRun.pushing.description": "Envoi du moniteur vers le service...", "xpack.synthetics.testRun.runErrorLabel": "Erreur lors de l'exécution du test", "xpack.synthetics.testRunDetailsRoute.page.title": "Détails de l'exécution du test", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 0ed53a0ef38fc..8800f9f6547d3 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -12158,11 +12158,9 @@ "xpack.enterpriseSearch.content.indices.configurationConnector.config.submitButton.title": "構成を保存", "xpack.enterpriseSearch.content.indices.configurationConnector.config.warning.title": "このコネクターは、Elasticインデックスに関連付けられています", "xpack.enterpriseSearch.content.indices.configurationConnector.configuration.successToast.title": "構成が更新されました", - "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.button.label": "コネクターリポジトリを探索", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.clientExamplesLink": "コネクタークライアントの例", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.configurationFileLink": "構成ファイル", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.connectorDeployedText": "コネクターを構成したら、コネクターをセルフマネージドインフラストラクチャーにデプロイします。", - "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.description.firstParagraph": "コネクターリポジトリには、複数のコネクタークライアントの例があり、当社のフレームワークを利用して、カスタムデータソースに対する高速デプロイを実施できるようになっています。", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.waitingForConnector.button.label": "今すぐ再確認", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.waitingForConnectorText": "コネクターエンタープライズ サーチに接続されていません。構成のトラブルシューティングを行い、ページを更新してください。", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.waitingForConnectorTitle": "コネクターを待機しています", @@ -17876,7 +17874,6 @@ "xpack.infra.openView.columnNames.actions": "アクション", "xpack.infra.openView.columnNames.name": "名前", "xpack.infra.openView.flyoutHeader": "保存されたビューの管理", - "xpack.infra.openView.loadButton": "ビューの読み込み", "xpack.infra.registerFeatures.infraOpsDescription": "共通のサーバー、コンテナー、サービスのインフラストラクチャメトリックとログを閲覧します。", "xpack.infra.registerFeatures.infraOpsTitle": "メトリック", "xpack.infra.registerFeatures.logsDescription": "ログをリアルタイムでストリーするか、コンソール式の UI で履歴ビューをスクロールします。", @@ -17886,10 +17883,8 @@ "xpack.infra.savedView.errorOnCreate.duplicateViewName": "その名前のビューはすでに存在します。", "xpack.infra.savedView.errorOnCreate.title": "ビューの保存中にエラーが発生しました。", "xpack.infra.savedView.findError.title": "ビューの読み込み中にエラーが発生しました。", - "xpack.infra.savedView.loadView": "ビューの読み込み", "xpack.infra.savedView.manageViews": "ビューの管理", "xpack.infra.savedView.saveNewView": "新しいビューの保存", - "xpack.infra.savedView.searchPlaceholder": "保存されたビューの検索", "xpack.infra.savedView.unknownView": "ビューが選択されていません", "xpack.infra.savedView.updateView": "ビューの更新", "xpack.infra.showHistory": "履歴を表示", @@ -18002,7 +17997,6 @@ "xpack.infra.waffle.region": "すべて", "xpack.infra.waffle.regionLabel": "地域", "xpack.infra.waffle.savedView.createHeader": "ビューを保存", - "xpack.infra.waffle.savedView.selectViewHeader": "読み込むビューを選択", "xpack.infra.waffle.savedView.updateHeader": "ビューの更新", "xpack.infra.waffle.savedViews.cancel": "キャンセル", "xpack.infra.waffle.savedViews.cancelButton": "キャンセル", @@ -34696,9 +34690,7 @@ "xpack.synthetics.monitorManagement.editMonitorCrumb": "モニターを編集", "xpack.synthetics.monitorManagement.editMonitorError": "モニター管理の読み込みエラー", "xpack.synthetics.monitorManagement.editMonitorError.description": "モニター管理設定を読み込めませんでした。サポートに問い合わせてください。", - "xpack.synthetics.monitorManagement.emptyState.enablement": "モニター管理を有効にすると、世界中のホスティングされたテスト場所から軽量でリアルなブラウザーモニターを実行できます。モニター管理を有効にすると、APIキーが生成され、SyntheticsサービスはElasticsearchクラスターに書き込むことができます。", "xpack.synthetics.monitorManagement.emptyState.enablement.doc": "ドキュメントを読む", - "xpack.synthetics.monitorManagement.emptyState.enablement.enabled.title": "モニター管理を有効にする", "xpack.synthetics.monitorManagement.emptyState.enablement.learnMore": "詳細について", "xpack.synthetics.monitorManagement.emptyState.enablement.title": "有効にする", "xpack.synthetics.monitorManagement.failed": "失敗", @@ -34764,9 +34756,7 @@ "xpack.synthetics.monitorManagement.startAddingLocationsDescription": "非公開の場所では、独自の施設からモニターを実行できます。Fleet経由で制御および保守できるElasticエージェントとエージェントポリシーが必要です。", "xpack.synthetics.monitorManagement.steps": "ステップ", "xpack.synthetics.monitorManagement.summary.heading": "まとめ", - "xpack.synthetics.monitorManagement.syntheticsDisabledSuccess": "モニター管理は正常に無効にされました。", "xpack.synthetics.monitorManagement.syntheticsEnableLabel.management": "モニター管理を有効にする", - "xpack.synthetics.monitorManagement.syntheticsEnableSuccess": "モニター管理は正常に有効にされました。", "xpack.synthetics.monitorManagement.testResult": "テスト結果", "xpack.synthetics.monitorManagement.testResults": "テスト結果", "xpack.synthetics.monitorManagement.testRuns.label": "テスト実行", @@ -35150,7 +35140,6 @@ "xpack.synthetics.testResults.expandedRow.response_body.notRecorded": "本文が記録されていません。モニター設定の詳細オプションで、インデックス応答オプションを[常時]に設定して、本文を記録します。", "xpack.synthetics.testRun.description": "モニターをテストし、保存する前に結果を検証します", "xpack.synthetics.testRun.pushError": "モニターをサービスにプッシュできませんでした。", - "xpack.synthetics.testRun.pushErrorLabel": "プッシュエラー", "xpack.synthetics.testRun.pushing.description": "モニターをサービスにプッシュしています...", "xpack.synthetics.testRun.runErrorLabel": "テストの実行エラー", "xpack.synthetics.testRunDetailsRoute.page.title": "テスト実行詳細", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index d88f43949ac7c..90564505290d1 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -12160,11 +12160,9 @@ "xpack.enterpriseSearch.content.indices.configurationConnector.config.submitButton.title": "保存配置", "xpack.enterpriseSearch.content.indices.configurationConnector.config.warning.title": "此连接器将绑定到您的 Elastic 索引", "xpack.enterpriseSearch.content.indices.configurationConnector.configuration.successToast.title": "已更新配置", - "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.button.label": "浏览连接器存储库", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.clientExamplesLink": "连接器客户端示例", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.configurationFileLink": "配置文件", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.connectorDeployedText": "配置连接器后,请将其部署到您的自管型基础架构。", - "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.description.firstParagraph": "连接器存储库包含几个连接器客户端示例,有助于您利用我们的框架针对定制数据源进行加速开发。", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.waitingForConnector.button.label": "立即重新检查", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.waitingForConnectorText": "您的连接器尚未连接到 Enterprise Search。排除配置故障并刷新页面。", "xpack.enterpriseSearch.content.indices.configurationConnector.connectorPackage.waitingForConnectorTitle": "等候您的连接器", @@ -17878,7 +17876,6 @@ "xpack.infra.openView.columnNames.actions": "操作", "xpack.infra.openView.columnNames.name": "名称", "xpack.infra.openView.flyoutHeader": "管理已保存视图", - "xpack.infra.openView.loadButton": "加载视图", "xpack.infra.registerFeatures.infraOpsDescription": "浏览常用服务器、容器和服务的基础设施指标和日志。", "xpack.infra.registerFeatures.infraOpsTitle": "指标", "xpack.infra.registerFeatures.logsDescription": "实时流式传输日志或在类似控制台的工具中滚动浏览历史视图。", @@ -17888,10 +17885,8 @@ "xpack.infra.savedView.errorOnCreate.duplicateViewName": "具有该名称的视图已存在。", "xpack.infra.savedView.errorOnCreate.title": "保存视图时出错。", "xpack.infra.savedView.findError.title": "加载视图时出错。", - "xpack.infra.savedView.loadView": "加载视图", "xpack.infra.savedView.manageViews": "管理视图", "xpack.infra.savedView.saveNewView": "保存新视图", - "xpack.infra.savedView.searchPlaceholder": "搜索已保存视图", "xpack.infra.savedView.unknownView": "未选择视图", "xpack.infra.savedView.updateView": "更新视图", "xpack.infra.showHistory": "显示历史记录", @@ -18004,7 +17999,6 @@ "xpack.infra.waffle.region": "全部", "xpack.infra.waffle.regionLabel": "地区", "xpack.infra.waffle.savedView.createHeader": "保存视图", - "xpack.infra.waffle.savedView.selectViewHeader": "选择要加载的视图", "xpack.infra.waffle.savedView.updateHeader": "更新视图", "xpack.infra.waffle.savedViews.cancel": "取消", "xpack.infra.waffle.savedViews.cancelButton": "取消", @@ -34713,9 +34707,7 @@ "xpack.synthetics.monitorManagement.editMonitorCrumb": "编辑监测", "xpack.synthetics.monitorManagement.editMonitorError": "加载监测管理时出错", "xpack.synthetics.monitorManagement.editMonitorError.description": "无法加载监测管理设置。请联系支持人员。", - "xpack.synthetics.monitorManagement.emptyState.enablement": "启用监测管理以从全球托管测试地点运行轻量级、真正的浏览器监测。启用监测管理将生成 API 密钥,以便 Synthetics 服务回写 Elasticsearch 集群。", "xpack.synthetics.monitorManagement.emptyState.enablement.doc": "阅读文档", - "xpack.synthetics.monitorManagement.emptyState.enablement.enabled.title": "启用监测管理", "xpack.synthetics.monitorManagement.emptyState.enablement.learnMore": "希望了解详情?", "xpack.synthetics.monitorManagement.emptyState.enablement.title": "启用", "xpack.synthetics.monitorManagement.failed": "失败", @@ -34781,9 +34773,7 @@ "xpack.synthetics.monitorManagement.startAddingLocationsDescription": "专用位置供您从自己的场所运行监测。它们需要可以通过 Fleet 进行控制和维护的 Elastic 代理和代理策略。", "xpack.synthetics.monitorManagement.steps": "步长", "xpack.synthetics.monitorManagement.summary.heading": "摘要", - "xpack.synthetics.monitorManagement.syntheticsDisabledSuccess": "已成功禁用监测管理。", "xpack.synthetics.monitorManagement.syntheticsEnableLabel.management": "启用监测管理", - "xpack.synthetics.monitorManagement.syntheticsEnableSuccess": "已成功启用监测管理。", "xpack.synthetics.monitorManagement.testResult": "测试结果", "xpack.synthetics.monitorManagement.testResults": "测试结果", "xpack.synthetics.monitorManagement.testRuns.label": "测试运行", @@ -35167,7 +35157,6 @@ "xpack.synthetics.testResults.expandedRow.response_body.notRecorded": "正文未记录。在监测配置的高级选项中将索引响应正文选项设置为“始终打开”以记录正文。", "xpack.synthetics.testRun.description": "请在保存前测试您的监测并验证结果", "xpack.synthetics.testRun.pushError": "无法推送监测到服务。", - "xpack.synthetics.testRun.pushErrorLabel": "推送错误", "xpack.synthetics.testRun.pushing.description": "正在推送监测到服务......", "xpack.synthetics.testRun.runErrorLabel": "运行测试时出错", "xpack.synthetics.testRunDetailsRoute.page.title": "测试运行详情", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_query.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_query.tsx index 682bdbb52d81d..4d99754eb2331 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_query.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_query.tsx @@ -77,6 +77,7 @@ export const ActionAlertsFilterQuery: React.FC = ( query={query.kql} filters={query.filters ?? []} onQueryChange={onQueryChange} + onQuerySubmit={onQueryChange} onFiltersUpdated={onFiltersUpdated} showFilterBar submitOnBlur 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 7557adb4979c0..f5245cd85fb53 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 @@ -562,6 +562,10 @@ export const ActionForm = ({ actionType={actionTypesIndex[activeActionItem.actionTypeId]} onClose={closeAddConnectorModal} postSaveEventHandler={(savedAction: ActionConnector) => { + // TODO: fix in https://github.com/elastic/kibana/issues/155993 + // actionTypes with subtypes need to be updated in case they switched to a + // subtype that is not the default one + actions[0].actionTypeId = savedAction.actionTypeId; connectors.push(savedAction); const indicesToUpdate = activeActionItem.indices || []; indicesToUpdate.forEach((index: number) => setActionIdByIndex(savedAction.id, index)); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx index 49b1f9905cd7f..64c48606ff916 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx @@ -21,6 +21,7 @@ export function AlertsSearchBar({ query, filters, onQueryChange, + onQuerySubmit, onFiltersUpdated, rangeFrom, rangeTo, @@ -39,9 +40,20 @@ export function AlertsSearchBar({ const [queryLanguage, setQueryLanguage] = useState('kuery'); const { value: dataView, loading, error } = useAlertDataView(featureIds); - const onQuerySubmit = useCallback( + const onSearchQuerySubmit = useCallback( ({ dateRange, query: nextQuery }: { dateRange: TimeRange; query?: Query }) => { - onQueryChange({ + onQuerySubmit({ + dateRange, + query: typeof nextQuery?.query === 'string' ? nextQuery.query : undefined, + }); + setQueryLanguage((nextQuery?.language ?? 'kuery') as QueryLanguageType); + }, + [onQuerySubmit, setQueryLanguage] + ); + + const onSearchQueryChange = useCallback( + ({ dateRange, query: nextQuery }: { dateRange: TimeRange; query?: Query }) => { + onQueryChange?.({ dateRange, query: typeof nextQuery?.query === 'string' ? nextQuery.query : undefined, }); @@ -50,7 +62,7 @@ export function AlertsSearchBar({ [onQueryChange, setQueryLanguage] ); const onRefresh = ({ dateRange }: { dateRange: TimeRange }) => { - onQueryChange({ + onQuerySubmit({ dateRange, }); }; @@ -66,13 +78,13 @@ export function AlertsSearchBar({ dateRangeTo={rangeTo} displayStyle="inPage" showFilterBar={showFilterBar} - onQuerySubmit={onQuerySubmit} + onQuerySubmit={onSearchQuerySubmit} onFiltersUpdated={onFiltersUpdated} onRefresh={onRefresh} showDatePicker={showDatePicker} showSubmitButton={showSubmitButton} submitOnBlur={submitOnBlur} - onQueryChange={onQuerySubmit} + onQueryChange={onSearchQueryChange} /> ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/types.ts index b7616333c199a..958b0e05cbf3a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/types.ts @@ -22,7 +22,11 @@ export interface AlertsSearchBarProps { showSubmitButton?: boolean; placeholder?: string; submitOnBlur?: boolean; - onQueryChange: (query: { + onQueryChange?: (query: { + dateRange: { from: string; to: string; mode?: 'absolute' | 'relative' }; + query?: string; + }) => void; + onQuerySubmit: (query: { dateRange: { from: string; to: string; mode?: 'absolute' | 'relative' }; query?: string; }) => void; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx index 3612beb93102c..0ccc05bac858a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx @@ -40,6 +40,12 @@ const DefaultGridStyle: EuiDataGridStyle = { fontSize: 's', }; +const getCellActionsStub = { + getCellActions: () => null, + visibleCellActions: undefined, + disabledCellActions: [], +}; + const basicRenderCellValue = ({ data, columnId, @@ -379,7 +385,7 @@ const AlertsTable: React.FunctionComponent = (props: AlertsTab dataGridRef, pageSize: pagination.pageSize, }) - : { getCellActions: () => null, visibleCellActions: undefined, disabledCellActions: [] }; + : getCellActionsStub; const columnsWithCellActions = useMemo(() => { if (getCellActions) { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.tsx index e9123776a2144..44c560a732b7a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.tsx @@ -323,13 +323,18 @@ const AlertsTableStateWithQueryProvider = ({ const CasesContext = casesService?.ui.getCasesContext(); const isCasesContextAvailable = casesService && CasesContext; + const memoizedCases = useMemo( + () => ({ + data: cases ?? new Map(), + isLoading: isLoadingCases, + }), + [cases, isLoadingCases] + ); + const tableProps: AlertsTableProps = useMemo( () => ({ alertsTableConfiguration, - cases: { - data: cases ?? new Map(), - isLoading: isLoadingCases, - }, + cases: memoizedCases, columns, bulkActions: [], deletedEventIds: [], @@ -362,8 +367,7 @@ const AlertsTableStateWithQueryProvider = ({ }), [ alertsTableConfiguration, - cases, - isLoadingCases, + memoizedCases, columns, flyoutSize, pagination.pageSize, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/use_columns.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/use_columns.ts index 9caea0dfd26b0..1699a86b78cb6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/use_columns.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/use_columns.ts @@ -233,9 +233,13 @@ export const useColumns = ({ [columns] ); + const visibleColumns = useMemo(() => { + return getColumnIds(columns); + }, [columns]); + return { columns, - visibleColumns: getColumnIds(columns), + visibleColumns, isBrowserFieldDataLoading, browserFields, onColumnsChange: setColumnsAndSave, 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 94d6b1b398824..b83d2f068e592 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 @@ -76,6 +76,7 @@ export const RuleEdit = ({ onClose, reloadRules, onSave, + hideInterval, ruleTypeRegistry, actionTypeRegistry, metadata: initialMetadata, @@ -233,6 +234,7 @@ export const RuleEdit = ({ dispatch={dispatch} errors={ruleErrors} actionTypeRegistry={actionTypeRegistry} + hideInterval={hideInterval} ruleTypeRegistry={ruleTypeRegistry} canChangeTrigger={false} setHasActionsDisabled={setHasActionsDisabled} diff --git a/x-pack/test/alerting_api_integration/common/lib/get_event_log.ts b/x-pack/test/alerting_api_integration/common/lib/get_event_log.ts index 912e153f9a8c0..0ea5a2601f75a 100644 --- a/x-pack/test/alerting_api_integration/common/lib/get_event_log.ts +++ b/x-pack/test/alerting_api_integration/common/lib/get_event_log.ts @@ -39,7 +39,7 @@ export async function getEventLog(params: GetEventLogParams): Promise { diff --git a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/routes.ts b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/routes.ts index 135d3c02b9f8f..28e0abd955b2c 100644 --- a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/routes.ts +++ b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/routes.ts @@ -24,6 +24,7 @@ import { TaskManagerStartContract, } from '@kbn/task-manager-plugin/server'; import { SECURITY_EXTENSION_ID, SPACES_EXTENSION_ID } from '@kbn/core-saved-objects-server'; +import { queryOptionsSchema } from '@kbn/event-log-plugin/server/event_log_client'; import { FixtureStartDeps } from './plugin'; import { retryIfConflicts } from './lib/retry_if_conflicts'; @@ -449,4 +450,37 @@ export function defineRoutes( } } ); + + router.get( + { + path: '/_test/event_log/{type}/{id}/_find', + validate: { + params: schema.object({ + type: schema.string(), + id: schema.string(), + }), + query: queryOptionsSchema, + }, + }, + async ( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> => { + const [, { eventLog }] = await core.getStartServices(); + const eventLogClient = eventLog.getClient(req); + const { + params: { id, type }, + query, + } = req; + + try { + return res.ok({ + body: await eventLogClient.findEventsBySavedObjectIds(type, [id], query), + }); + } catch (err) { + return res.notFound(); + } + } + ); } diff --git a/x-pack/test/api_integration/apis/ml/results/get_categorizer_stats.ts b/x-pack/test/api_integration/apis/ml/results/get_categorizer_stats.ts index 3a515321f1a3d..92b2fe2a1401f 100644 --- a/x-pack/test/api_integration/apis/ml/results/get_categorizer_stats.ts +++ b/x-pack/test/api_integration/apis/ml/results/get_categorizer_stats.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { Datafeed } from '@kbn/ml-plugin/common/types/anomaly_detection_jobs'; -import { AnomalyCategorizerStatsDoc } from '@kbn/ml-plugin/common/types/anomalies'; +import type { MlAnomalyCategorizerStatsDoc } from '@kbn/ml-anomaly-utils'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { USER } from '../../../../functional/services/ml/security_common'; import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api'; @@ -71,7 +71,7 @@ export default ({ getService }: FtrProviderContext) => { .set(COMMON_REQUEST_HEADERS); ml.api.assertResponseStatusCode(200, status, body); - body.forEach((doc: AnomalyCategorizerStatsDoc) => { + body.forEach((doc: MlAnomalyCategorizerStatsDoc) => { expect(doc.job_id).to.eql(jobId); expect(doc.result_type).to.eql('categorizer_stats'); expect(doc.partition_field_name).to.be(PARTITION_FIELD_NAME); @@ -86,7 +86,7 @@ export default ({ getService }: FtrProviderContext) => { .set(COMMON_REQUEST_HEADERS); ml.api.assertResponseStatusCode(200, status, body); - body.forEach((doc: AnomalyCategorizerStatsDoc) => { + body.forEach((doc: MlAnomalyCategorizerStatsDoc) => { expect(doc.job_id).to.eql(jobId); expect(doc.result_type).to.eql('categorizer_stats'); expect(doc.partition_field_name).to.be(PARTITION_FIELD_NAME); @@ -113,7 +113,7 @@ export default ({ getService }: FtrProviderContext) => { .set(COMMON_REQUEST_HEADERS); ml.api.assertResponseStatusCode(200, status, body); - body.forEach((doc: AnomalyCategorizerStatsDoc) => { + body.forEach((doc: MlAnomalyCategorizerStatsDoc) => { expect(doc.job_id).to.eql(jobId); expect(doc.result_type).to.eql('categorizer_stats'); expect(doc.partition_field_name).to.be(PARTITION_FIELD_NAME); @@ -129,7 +129,7 @@ export default ({ getService }: FtrProviderContext) => { .set(COMMON_REQUEST_HEADERS); ml.api.assertResponseStatusCode(200, status, body); - body.forEach((doc: AnomalyCategorizerStatsDoc) => { + body.forEach((doc: MlAnomalyCategorizerStatsDoc) => { expect(doc.job_id).to.eql(jobId); expect(doc.result_type).to.eql('categorizer_stats'); expect(doc.partition_field_name).to.be(PARTITION_FIELD_NAME); diff --git a/x-pack/test/api_integration/apis/synthetics/synthetics_enablement.ts b/x-pack/test/api_integration/apis/synthetics/synthetics_enablement.ts index bf68da4c148f5..3a0713431efb4 100644 --- a/x-pack/test/api_integration/apis/synthetics/synthetics_enablement.ts +++ b/x-pack/test/api_integration/apis/synthetics/synthetics_enablement.ts @@ -248,7 +248,7 @@ export default function ({ getService }: FtrProviderContext) { overwrite: true, attributes: { id: apiKeyResult.body.id, - name: 'synthetics-api-key (required for monitor management)', + name: 'synthetics-api-key (required for Synthetics App)', apiKey: apiKeyResult.body.api_key, }, }); diff --git a/x-pack/test/apm_api_integration/tests/alerts/anomaly_alert.spec.ts b/x-pack/test/apm_api_integration/tests/alerts/anomaly_alert.spec.ts index a9593658feabc..6464bafd26397 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/anomaly_alert.spec.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/anomaly_alert.spec.ts @@ -9,7 +9,7 @@ import { ApmRuleType } from '@kbn/apm-plugin/common/rules/apm_rule_types'; import { apm, timerange } from '@kbn/apm-synthtrace-client'; import expect from '@kbn/expect'; import { range } from 'lodash'; -import { ANOMALY_SEVERITY } from '@kbn/apm-plugin/common/ml_constants'; +import { ML_ANOMALY_SEVERITY } from '@kbn/ml-anomaly-utils/anomaly_severity'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createAndRunApmMlJobs } from '../../common/utils/create_and_run_apm_ml_jobs'; import { createApmRule } from './alerting_api_helper'; @@ -90,7 +90,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { environment: 'production', windowSize: 99, windowUnit: 'y', - anomalySeverityType: ANOMALY_SEVERITY.WARNING, + anomalySeverityType: ML_ANOMALY_SEVERITY.WARNING, }, ruleTypeId: ApmRuleType.Anomaly, }); diff --git a/x-pack/test/fleet_api_integration/apis/epm/bulk_install.ts b/x-pack/test/fleet_api_integration/apis/epm/bulk_install.ts new file mode 100644 index 0000000000000..7b483c06e36b5 --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/epm/bulk_install.ts @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; +import { skipIfNoDockerRegistry } from '../../helpers'; +import { setupFleetAndAgents } from '../agents/services'; + +export default function (providerContext: FtrProviderContext) { + const { getService } = providerContext; + const supertest = getService('supertest'); + + const pkgName = 'multiple_versions'; + const pkgOlderVersion = '0.1.0'; + const pkgLatestVersion = '0.3.0'; + + const uninstallPackage = async (name: string, version: string) => { + await supertest.delete(`/api/fleet/epm/packages/${name}/${version}`).set('kbn-xsrf', 'xxxx'); + }; + + describe('bulk package install api', async () => { + skipIfNoDockerRegistry(providerContext); + setupFleetAndAgents(providerContext); + + it('should install the latest version by default', async () => { + const response = await supertest + .post(`/api/fleet/epm/packages/_bulk?prerelease=true`) + .set('kbn-xsrf', 'xxxx') + .send({ packages: [pkgName] }) + .expect(200); + + expect(response.body.items.length).equal(1); + expect(response.body.items[0].version).equal(pkgLatestVersion); + + await uninstallPackage(pkgName, pkgLatestVersion); + }); + + it('should install an older version if force is true', async () => { + const response = await supertest + .post(`/api/fleet/epm/packages/_bulk?prerelease=true`) + .set('kbn-xsrf', 'xxxx') + .send({ packages: [{ name: pkgName, version: pkgOlderVersion }], force: true }) + .expect(200); + + expect(response.body.items.length).equal(1); + expect(response.body.items[0].version).equal(pkgOlderVersion); + + await uninstallPackage(pkgName, pkgOlderVersion); + }); + + it('should reject installing an older version if force is false', async () => { + const response = await supertest + .post(`/api/fleet/epm/packages/_bulk?prerelease=true`) + .set('kbn-xsrf', 'xxxx') + .send({ packages: [{ name: pkgName, version: pkgOlderVersion }] }) + .expect(200); + + expect(response.body.response[0].statusCode).equal(400); + expect(response.body.response[0].error).equal( + 'multiple_versions-0.1.0 is out-of-date and cannot be installed or updated' + ); + }); + }); +} diff --git a/x-pack/test/functional/apps/infra/home_page.ts b/x-pack/test/functional/apps/infra/home_page.ts index 54435f11a33e1..13acd911cfbea 100644 --- a/x-pack/test/functional/apps/infra/home_page.ts +++ b/x-pack/test/functional/apps/infra/home_page.ts @@ -19,6 +19,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const retry = getService('retry'); const pageObjects = getPageObjects(['common', 'header', 'infraHome', 'infraSavedViews']); const kibanaServer = getService('kibanaServer'); + const testSubjects = getService('testSubjects'); describe('Home page', function () { this.tags('includeFirefox'); @@ -220,36 +221,54 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.infraHome.ensurePopoverClosed(); }); }); - // Failing: See https://github.com/elastic/kibana/issues/106650 - describe.skip('Saved Views', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs')); - it('should have save and load controls', async () => { + + describe('Saved Views', () => { + before(async () => { + esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs'); await pageObjects.common.navigateToApp('infraOps'); await pageObjects.infraHome.waitForLoading(); - await pageObjects.infraHome.goToTime(DATE_WITH_DATA); - await pageObjects.infraSavedViews.getSavedViewsButton(); + }); + + after(() => esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs')); + + it('should render a button with the view name', async () => { await pageObjects.infraSavedViews.ensureViewIsLoaded('Default view'); }); - it('should open popover', async () => { + it('should open/close the views popover menu on button click', async () => { await pageObjects.infraSavedViews.clickSavedViewsButton(); + testSubjects.existOrFail('savedViews-popover'); await pageObjects.infraSavedViews.closeSavedViewsPopover(); }); - it('should create new saved view and load it', async () => { - await pageObjects.infraSavedViews.clickSavedViewsButton(); - await pageObjects.infraSavedViews.clickSaveNewViewButton(); - await pageObjects.infraSavedViews.getCreateSavedViewModal(); - await pageObjects.infraSavedViews.createNewSavedView('view1'); + it('should create a new saved view and load it', async () => { + await pageObjects.infraSavedViews.createView('view1'); await pageObjects.infraSavedViews.ensureViewIsLoaded('view1'); }); - it('should new views should be listed in the load views list', async () => { - await pageObjects.infraSavedViews.clickSavedViewsButton(); - await pageObjects.infraSavedViews.clickLoadViewButton(); - await pageObjects.infraSavedViews.ensureViewIsLoadable('view1'); - await pageObjects.infraSavedViews.closeSavedViewsLoadModal(); + it('should laod a clicked view from the manage views section', async () => { + await pageObjects.infraSavedViews.ensureViewIsLoaded('view1'); + const views = await pageObjects.infraSavedViews.getManageViewsEntries(); + await views[0].click(); + await pageObjects.infraSavedViews.ensureViewIsLoaded('Default view'); + }); + + it('should update the current saved view and load it', async () => { + let views = await pageObjects.infraSavedViews.getManageViewsEntries(); + expect(views.length).to.equal(2); + await pageObjects.infraSavedViews.pressEsc(); + + await pageObjects.infraSavedViews.createView('view2'); + await pageObjects.infraSavedViews.ensureViewIsLoaded('view2'); + views = await pageObjects.infraSavedViews.getManageViewsEntries(); + expect(views.length).to.equal(3); + await pageObjects.infraSavedViews.pressEsc(); + + await pageObjects.infraSavedViews.updateView('view3'); + await pageObjects.infraSavedViews.ensureViewIsLoaded('view3'); + views = await pageObjects.infraSavedViews.getManageViewsEntries(); + expect(views.length).to.equal(3); + await pageObjects.infraSavedViews.pressEsc(); }); }); }); diff --git a/x-pack/test/functional/apps/infra/hosts_view.ts b/x-pack/test/functional/apps/infra/hosts_view.ts index e9000a9cf3e6d..a0b11335d9768 100644 --- a/x-pack/test/functional/apps/infra/hosts_view.ts +++ b/x-pack/test/functional/apps/infra/hosts_view.ts @@ -149,9 +149,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const enableHostView = () => pageObjects.infraHostsView.clickEnableHostViewButton(); // Tests - - // Failing: See https://github.com/elastic/kibana/issues/155429 - describe.skip('Hosts View', function () { + describe('Hosts View', function () { this.tags('includeFirefox'); before(async () => { @@ -328,6 +326,13 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { START_DATE.format(timepickerFormat), END_DATE.format(timepickerFormat) ); + + await retry.waitFor( + 'wait for table and KPI charts to load', + async () => + (await pageObjects.infraHostsView.isHostTableLoading()) && + (await pageObjects.infraHostsView.isKPIChartsLoaded()) + ); }); after(async () => { @@ -361,7 +366,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('KPI tiles', () => { it('should render 5 metrics trend tiles', async () => { - const hosts = await pageObjects.infraHostsView.getAllMetricsTrendTiles(); + const hosts = await pageObjects.infraHostsView.getAllKPITiles(); expect(hosts.length).to.equal(5); }); @@ -373,15 +378,17 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { { metric: 'rx', value: 'N/A' }, ].forEach(({ metric, value }) => { it(`${metric} tile should show ${value}`, async () => { - const tileValue = await pageObjects.infraHostsView.getMetricsTrendTileValue(metric); - expect(tileValue).to.eql(value); + await retry.try(async () => { + const tileValue = await pageObjects.infraHostsView.getKPITileValue(metric); + expect(tileValue).to.eql(value); + }); }); }); }); describe('Metrics Tab', () => { before(async () => { - browser.scrollTop(); + await browser.scrollTop(); await pageObjects.infraHostsView.visitMetricsTab(); }); @@ -391,18 +398,18 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should have an option to open the chart in lens', async () => { - await pageObjects.infraHostsView.getOpenInLensOption(); + await pageObjects.infraHostsView.clickAndValidateMetriChartActionOptions(); }); }); describe('Logs Tab', () => { before(async () => { - browser.scrollTop(); + await browser.scrollTop(); await pageObjects.infraHostsView.visitLogsTab(); }); it('should load the Logs tab section when clicking on it', async () => { - testSubjects.existOrFail('hostsView-logs'); + await testSubjects.existOrFail('hostsView-logs'); }); }); @@ -413,7 +420,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const COLUMNS = 5; before(async () => { - browser.scrollTop(); + await browser.scrollTop(); await pageObjects.infraHostsView.visitAlertTab(); }); @@ -474,7 +481,14 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const query = filtererEntries.map((entry) => `host.name :"${entry.title}"`).join(' or '); before(async () => { + await browser.scrollTop(); await pageObjects.infraHostsView.submitQuery(query); + await retry.waitFor( + 'wait for table and KPI charts to load', + async () => + (await pageObjects.infraHostsView.isHostTableLoading()) && + (await pageObjects.infraHostsView.isKPIChartsLoaded()) + ); }); after(async () => { @@ -502,8 +516,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { { metric: 'tx', value: 'N/A' }, { metric: 'rx', value: 'N/A' }, ].map(async ({ metric, value }) => { - const tileValue = await pageObjects.infraHostsView.getMetricsTrendTileValue(metric); - expect(tileValue).to.eql(value); + await retry.try(async () => { + const tileValue = await pageObjects.infraHostsView.getKPITileValue(metric); + expect(tileValue).to.eql(value); + }); }) ); }); @@ -531,6 +547,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); describe('Pagination and Sorting', () => { + before(async () => { + await browser.scrollTop(); + }); + beforeEach(async () => { await pageObjects.infraHostsView.changePageSize(5); }); diff --git a/x-pack/test/functional/apps/infra/metrics_explorer.ts b/x-pack/test/functional/apps/infra/metrics_explorer.ts index 4d6859a4e99e7..650040b95f420 100644 --- a/x-pack/test/functional/apps/infra/metrics_explorer.ts +++ b/x-pack/test/functional/apps/infra/metrics_explorer.ts @@ -24,6 +24,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { 'timePicker', 'infraSavedViews', ]); + const testSubjects = getService('testSubjects'); + const retry = getService('retry'); describe('Metrics Explorer', function () { this.tags('includeFirefox'); @@ -78,8 +80,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should display multple charts', async () => { - const charts = await pageObjects.infraMetricsExplorer.getCharts(); - expect(charts.length).to.equal(6); + await retry.try(async () => { + const charts = await pageObjects.infraMetricsExplorer.getCharts(); + expect(charts.length).to.equal(6); + }); }); it('should render as area chart by default', async () => { @@ -97,35 +101,51 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); describe('Saved Views', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs')); + before(async () => { + esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs'); + await pageObjects.infraHome.goToMetricExplorer(); + }); + after(() => esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs')); - describe('save functionality', () => { - it('should have saved views component', async () => { - await pageObjects.common.navigateToApp('infraOps'); - await pageObjects.infraHome.goToMetricExplorer(); - await pageObjects.infraSavedViews.getSavedViewsButton(); - await pageObjects.infraSavedViews.ensureViewIsLoaded('Default view'); - }); - it('should open popover', async () => { - await pageObjects.infraSavedViews.clickSavedViewsButton(); - await pageObjects.infraSavedViews.closeSavedViewsPopover(); - }); + it('should render a button with the view name', async () => { + await pageObjects.infraSavedViews.ensureViewIsLoaded('Default view'); + }); - it('should create new saved view and load it', async () => { - await pageObjects.infraSavedViews.clickSavedViewsButton(); - await pageObjects.infraSavedViews.clickSaveNewViewButton(); - await pageObjects.infraSavedViews.getCreateSavedViewModal(); - await pageObjects.infraSavedViews.createNewSavedView('view1'); - await pageObjects.infraSavedViews.ensureViewIsLoaded('view1'); - }); + it('should open/close the views popover menu on button click', async () => { + await pageObjects.infraSavedViews.clickSavedViewsButton(); + testSubjects.existOrFail('savedViews-popover'); + await pageObjects.infraSavedViews.closeSavedViewsPopover(); + }); - it('should new views should be listed in the load views list', async () => { - await pageObjects.infraSavedViews.clickSavedViewsButton(); - await pageObjects.infraSavedViews.clickLoadViewButton(); - await pageObjects.infraSavedViews.ensureViewIsLoadable('view1'); - await pageObjects.infraSavedViews.closeSavedViewsLoadModal(); - }); + it('should create a new saved view and load it', async () => { + await pageObjects.infraSavedViews.createView('view1'); + await pageObjects.infraSavedViews.ensureViewIsLoaded('view1'); + }); + + it('should laod a clicked view from the manage views section', async () => { + await pageObjects.infraSavedViews.ensureViewIsLoaded('view1'); + const views = await pageObjects.infraSavedViews.getManageViewsEntries(); + await views[0].click(); + await pageObjects.infraSavedViews.ensureViewIsLoaded('Default view'); + }); + + it('should update the current saved view and load it', async () => { + let views = await pageObjects.infraSavedViews.getManageViewsEntries(); + expect(views.length).to.equal(2); + await pageObjects.infraSavedViews.pressEsc(); + + await pageObjects.infraSavedViews.createView('view2'); + await pageObjects.infraSavedViews.ensureViewIsLoaded('view2'); + views = await pageObjects.infraSavedViews.getManageViewsEntries(); + expect(views.length).to.equal(3); + await pageObjects.infraSavedViews.pressEsc(); + + await pageObjects.infraSavedViews.updateView('view3'); + await pageObjects.infraSavedViews.ensureViewIsLoaded('view3'); + views = await pageObjects.infraSavedViews.getManageViewsEntries(); + expect(views.length).to.equal(3); + await pageObjects.infraSavedViews.pressEsc(); }); }); }); diff --git a/x-pack/test/functional/apps/management/feature_controls/management_security.ts b/x-pack/test/functional/apps/management/feature_controls/management_security.ts index f6f71ad827d1c..d88e2fd8ebe7a 100644 --- a/x-pack/test/functional/apps/management/feature_controls/management_security.ts +++ b/x-pack/test/functional/apps/management/feature_controls/management_security.ts @@ -64,7 +64,13 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(sections).to.have.length(2); expect(sections[0]).to.eql({ sectionId: 'insightsAndAlerting', - sectionLinks: ['triggersActions', 'cases', 'triggersActionsConnectors', 'jobsListLink'], + sectionLinks: [ + 'triggersActions', + 'cases', + 'triggersActionsConnectors', + 'jobsListLink', + 'maintenanceWindows', + ], }); expect(sections[1]).to.eql({ sectionId: 'kibana', diff --git a/x-pack/test/functional/page_objects/infra_hosts_view.ts b/x-pack/test/functional/page_objects/infra_hosts_view.ts index 6478d208226ad..6b0efc8ce0944 100644 --- a/x-pack/test/functional/page_objects/infra_hosts_view.ts +++ b/x-pack/test/functional/page_objects/infra_hosts_view.ts @@ -73,8 +73,16 @@ export function InfraHostsViewProvider({ getService }: FtrProviderContext) { return await testSubjects.click('hostsView-enable-feature-button'); }, + async getHostsTable() { + return testSubjects.find('hostsView-table'); + }, + + async isHostTableLoading() { + return !(await testSubjects.exists('tbody[class*=euiBasicTableBodyLoading]')); + }, + async getHostsTableData() { - const table = await testSubjects.find('hostsView-table'); + const table = await this.getHostsTable(); return table.findAllByTestSubject('hostsView-tableRow'); }, @@ -95,7 +103,7 @@ export function InfraHostsViewProvider({ getService }: FtrProviderContext) { return cellContent.getVisibleText(); }, - async getMetricsTrendContainer() { + async getKPIContainer() { return testSubjects.find('hostsView-metricsTrend'); }, @@ -103,25 +111,14 @@ export function InfraHostsViewProvider({ getService }: FtrProviderContext) { return testSubjects.find('hostsView-metricChart'); }, - getMetricsTab() { + // MetricsTtab + async getMetricsTab() { return testSubjects.find('hostsView-tabs-metrics'); }, async visitMetricsTab() { const metricsTab = await this.getMetricsTab(); - metricsTab.click(); - }, - - async getAllMetricsTrendTiles() { - const container = await this.getMetricsTrendContainer(); - return container.findAllByCssSelector('[data-test-subj*="hostsView-metricsTrend-"]'); - }, - - async getMetricsTrendTileValue(type: string) { - const container = await this.getMetricsTrendContainer(); - const element = await container.findByTestSubject(`hostsView-metricsTrend-${type}`); - const div = await element.findByClassName('echMetricText__value'); - return await div.getAttribute('title'); + await metricsTab.click(); }, async getAllMetricsCharts() { @@ -129,14 +126,32 @@ export function InfraHostsViewProvider({ getService }: FtrProviderContext) { return container.findAllByCssSelector('[data-test-subj*="hostsView-metricChart-"]'); }, - async getOpenInLensOption() { - const metricCharts = await this.getAllMetricsCharts(); - const chart = metricCharts.at(-1)!; - await chart.moveMouseTo(); - const button = await testSubjects.findDescendant('embeddablePanelToggleMenuIcon', chart); + async clickAndValidateMetriChartActionOptions() { + const element = await testSubjects.find('hostsView-metricChart-diskIOWrite'); + await element.moveMouseTo(); + const button = await element.findByTestSubject('embeddablePanelToggleMenuIcon'); await button.click(); - await testSubjects.existOrFail('embeddablePanelContextMenuOpen'); - return testSubjects.existOrFail('embeddablePanelAction-openInLens'); + await testSubjects.existOrFail('embeddablePanelAction-openInLens'); + // forces the modal to close + await element.click(); + }, + + // KPIs + async isKPIChartsLoaded() { + return !(await testSubjects.exists( + '[data-test-subj=hostsView-metricsTrend] .echChartStatus[data-ech-render-complete=true]' + )); + }, + + async getAllKPITiles() { + const container = await this.getKPIContainer(); + return container.findAllByCssSelector('[data-test-subj*="hostsView-metricsTrend-"]'); + }, + + async getKPITileValue(type: string) { + const element = await testSubjects.find(`hostsView-metricsTrend-${type}`); + const div = await element.findByClassName('echMetricText__value'); + return await div.getAttribute('title'); }, // Flyout Tabs @@ -187,7 +202,7 @@ export function InfraHostsViewProvider({ getService }: FtrProviderContext) { async visitLogsTab() { const logsTab = await this.getLogsTab(); - logsTab.click(); + await logsTab.click(); }, async getLogEntries() { @@ -212,7 +227,7 @@ export function InfraHostsViewProvider({ getService }: FtrProviderContext) { async visitAlertTab() { const alertsTab = await this.getAlertsTab(); - alertsTab.click(); + await alertsTab.click(); }, setAlertStatusFilter(alertStatus?: AlertStatus) { @@ -232,22 +247,14 @@ export function InfraHostsViewProvider({ getService }: FtrProviderContext) { return testSubjects.find('queryInput'); }, - async clearQueryBar() { - const queryBar = await this.getQueryBar(); - - return queryBar.clearValueWithKeyboard(); - }, - async typeInQueryBar(query: string) { const queryBar = await this.getQueryBar(); - await queryBar.clearValueWithKeyboard(); return queryBar.type(query); }, async submitQuery(query: string) { await this.typeInQueryBar(query); - await testSubjects.click('querySubmitButton'); }, @@ -288,13 +295,13 @@ export function InfraHostsViewProvider({ getService }: FtrProviderContext) { async sortByDiskLatency() { const diskLatency = await this.getDiskLatencyHeader(); const button = await testSubjects.findDescendant('tableHeaderSortButton', diskLatency); - return button.click(); + await button.click(); }, async sortByTitle() { const titleHeader = await this.getTitleHeader(); const button = await testSubjects.findDescendant('tableHeaderSortButton', titleHeader); - return button.click(); + await button.click(); }, }; } diff --git a/x-pack/test/functional/page_objects/infra_saved_views.ts b/x-pack/test/functional/page_objects/infra_saved_views.ts index 839b10fef1c68..2097be3d083a8 100644 --- a/x-pack/test/functional/page_objects/infra_saved_views.ts +++ b/x-pack/test/functional/page_objects/infra_saved_views.ts @@ -15,57 +15,67 @@ export function InfraSavedViewsProvider({ getService }: FtrProviderContext) { const browser = getService('browser'); return { - async getSavedViewsButton() { - return await testSubjects.find('savedViews-openPopover'); + getSavedViewsButton() { + return testSubjects.find('savedViews-openPopover'); }, - async clickSavedViewsButton() { - return await testSubjects.click('savedViews-openPopover'); + clickSavedViewsButton() { + return testSubjects.click('savedViews-openPopover'); }, - async getSavedViewsPopoer() { - return await testSubjects.find('savedViews-popover'); + getSavedViewsPopover() { + return testSubjects.find('savedViews-popover'); + }, + + pressEsc() { + return browser.pressKeys([Key.ESCAPE]); }, async closeSavedViewsPopover() { await testSubjects.find('savedViews-popover'); - return await browser.pressKeys([Key.ESCAPE]); + return this.pressEsc(); + }, + + getLoadViewButton() { + return testSubjects.find('savedViews-loadView'); }, - async getLoadViewButton() { - return await testSubjects.find('savedViews-loadView'); + getManageViewsButton() { + return testSubjects.find('savedViews-manageViews'); }, - async clickLoadViewButton() { - return await testSubjects.click('savedViews-loadView'); + clickManageViewsButton() { + return testSubjects.click('savedViews-manageViews'); }, - async getManageViewsButton() { - return await testSubjects.find('savedViews-manageViews'); + getManageViewsFlyout() { + return testSubjects.find('loadViewsFlyout'); }, - async clickManageViewsButton() { - return await testSubjects.click('savedViews-manageViews'); + async getManageViewsEntries() { + await this.clickSavedViewsButton(); + await this.clickManageViewsButton(); + return testSubjects.findAll('infraRenderNameButton'); }, - async getUpdateViewButton() { - return await testSubjects.find('savedViews-updateView'); + getUpdateViewButton() { + return testSubjects.find('savedViews-updateView'); }, - async clickUpdateViewButton() { - return await testSubjects.click('savedViews-updateView'); + clickUpdateViewButton() { + return testSubjects.click('savedViews-updateView'); }, - async getSaveNewViewButton() { - return await testSubjects.find('savedViews-saveNewView'); + getSaveNewViewButton() { + return testSubjects.find('savedViews-saveNewView'); }, - async clickSaveNewViewButton() { - return await testSubjects.click('savedViews-saveNewView'); + clickSaveNewViewButton() { + return testSubjects.click('savedViews-saveNewView'); }, - async getCreateSavedViewModal() { - return await testSubjects.find('savedViews-upsertModal'); + getCreateSavedViewModal() { + return testSubjects.find('savedViews-upsertModal'); }, async createNewSavedView(name: string) { @@ -74,6 +84,18 @@ export function InfraSavedViewsProvider({ getService }: FtrProviderContext) { await testSubjects.missingOrFail('savedViews-upsertModal'); }, + async createView(name: string) { + await this.clickSavedViewsButton(); + await this.clickSaveNewViewButton(); + await this.createNewSavedView(name); + }, + + async updateView(name: string) { + await this.clickSavedViewsButton(); + await this.clickUpdateViewButton(); + await this.createNewSavedView(name); + }, + async ensureViewIsLoaded(name: string) { await retry.try(async () => { const subject = await testSubjects.find('savedViews-openPopover'); @@ -86,8 +108,8 @@ export function InfraSavedViewsProvider({ getService }: FtrProviderContext) { await subject.findByCssSelector(`li[title="${name}"]`); }, - async closeSavedViewsLoadModal() { - return await testSubjects.click('cancelSavedViewModal'); + closeSavedViewsLoadModal() { + return testSubjects.click('cancelSavedViewModal'); }, }; } diff --git a/x-pack/test/plugin_api_integration/plugins/event_log/server/init_routes.ts b/x-pack/test/plugin_api_integration/plugins/event_log/server/init_routes.ts index f4cbc4ad31ffc..f08a58848311e 100644 --- a/x-pack/test/plugin_api_integration/plugins/event_log/server/init_routes.ts +++ b/x-pack/test/plugin_api_integration/plugins/event_log/server/init_routes.ts @@ -6,6 +6,7 @@ */ import { + CoreSetup, RequestHandlerContext, KibanaRequest, KibanaResponseFactory, @@ -16,6 +17,9 @@ import { } from '@kbn/core/server'; import { IEventLogService, IEventLogger } from '@kbn/event-log-plugin/server'; import { IValidatedEvent } from '@kbn/event-log-plugin/server/types'; +import { schema } from '@kbn/config-schema'; +import { queryOptionsSchema } from '@kbn/event-log-plugin/server/event_log_client'; +import { EventLogFixtureStartDeps } from './plugin'; export const logEventRoute = (router: IRouter, eventLogger: IEventLogger, logger: Logger) => { router.post( @@ -205,3 +209,81 @@ export const isEventLogServiceLoggingEntriesRoute = ( } ); }; + +export const getEventLogRoute = (router: IRouter, core: CoreSetup) => { + router.get( + { + path: '/_test/event_log/{type}/{id}/_find', + validate: { + params: schema.object({ + type: schema.string(), + id: schema.string(), + }), + query: queryOptionsSchema, + }, + }, + async ( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> => { + const [, { eventLog }] = await core.getStartServices(); + const eventLogClient = eventLog.getClient(req); + const { + params: { id, type }, + query, + } = req; + + try { + return res.ok({ + body: await eventLogClient.findEventsBySavedObjectIds(type, [id], query), + }); + } catch (err) { + return res.notFound(); + } + } + ); +}; + +export const getEventLogByIdsRoute = ( + router: IRouter, + core: CoreSetup +) => { + router.post( + { + path: '/_test/event_log/{type}/_find', + validate: { + params: schema.object({ + type: schema.string(), + }), + query: queryOptionsSchema, + body: schema.object({ + ids: schema.arrayOf(schema.string(), { defaultValue: [] }), + legacyIds: schema.arrayOf(schema.string(), { defaultValue: [] }), + }), + }, + }, + router.handleLegacyErrors(async function ( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise { + const [, { eventLog }] = await core.getStartServices(); + const eventLogClient = eventLog.getClient(req); + + const { + params: { type }, + body: { ids, legacyIds }, + query, + } = req; + + try { + return res.ok({ + body: await eventLogClient.findEventsBySavedObjectIds(type, ids, query, legacyIds), + }); + } catch (err) { + return res.notFound(); + } + }) + ); +}; diff --git a/x-pack/test/plugin_api_integration/plugins/event_log/server/plugin.ts b/x-pack/test/plugin_api_integration/plugins/event_log/server/plugin.ts index 87bb7de7f2f41..cfb78197d3aef 100644 --- a/x-pack/test/plugin_api_integration/plugins/event_log/server/plugin.ts +++ b/x-pack/test/plugin_api_integration/plugins/event_log/server/plugin.ts @@ -15,6 +15,8 @@ import { isIndexingEntriesRoute, isEventLogServiceLoggingEntriesRoute, isEventLogServiceEnabledRoute, + getEventLogRoute, + getEventLogByIdsRoute, } from './init_routes'; // this plugin's dependendencies @@ -34,7 +36,7 @@ export class EventLogFixturePlugin this.logger = initializerContext.logger.get('plugins', 'eventLogFixture'); } - public setup(core: CoreSetup, { eventLog }: EventLogFixtureSetupDeps) { + public setup(core: CoreSetup, { eventLog }: EventLogFixtureSetupDeps) { const router = core.http.createRouter(); eventLog.registerProviderActions('event_log_fixture', ['test']); @@ -60,6 +62,8 @@ export class EventLogFixturePlugin isIndexingEntriesRoute(router, eventLog, this.logger); isEventLogServiceLoggingEntriesRoute(router, eventLog, this.logger); isEventLogServiceEnabledRoute(router, eventLog, this.logger); + getEventLogRoute(router, core); + getEventLogByIdsRoute(router, core); } public start() {} diff --git a/x-pack/test/plugin_api_integration/plugins/event_log/tsconfig.json b/x-pack/test/plugin_api_integration/plugins/event_log/tsconfig.json index f1fc9d8927258..1276cb52b0109 100644 --- a/x-pack/test/plugin_api_integration/plugins/event_log/tsconfig.json +++ b/x-pack/test/plugin_api_integration/plugins/event_log/tsconfig.json @@ -12,6 +12,7 @@ ], "kbn_references": [ "@kbn/core", - "@kbn/event-log-plugin" + "@kbn/event-log-plugin", + "@kbn/config-schema" ] } diff --git a/x-pack/test/plugin_api_integration/test_suites/event_log/public_api_integration.ts b/x-pack/test/plugin_api_integration/test_suites/event_log/public_api_integration.ts index 04c3907cde588..ed7d31efe1c10 100644 --- a/x-pack/test/plugin_api_integration/test_suites/event_log/public_api_integration.ts +++ b/x-pack/test/plugin_api_integration/test_suites/event_log/public_api_integration.ts @@ -240,7 +240,7 @@ export default function ({ getService }: FtrProviderContext) { query: Record = {} ) { const urlPrefix = urlPrefixFromNamespace(namespace); - const url = `${urlPrefix}/internal/event_log/event_log_test/${id}/_find${ + const url = `${urlPrefix}/_test/event_log/event_log_test/${id}/_find${ isEmpty(query) ? '' : `?${Object.entries(query) @@ -261,7 +261,7 @@ export default function ({ getService }: FtrProviderContext) { legacyIds: string[] = [] ) { const urlPrefix = urlPrefixFromNamespace(namespace); - const url = `${urlPrefix}/internal/event_log/event_log_test/_find${ + const url = `${urlPrefix}/_test/event_log/event_log_test/_find${ isEmpty(query) ? '' : `?${Object.entries(query) diff --git a/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts b/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts index dc244a51bb183..c5d95ba845302 100644 --- a/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts +++ b/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts @@ -260,7 +260,7 @@ export default function ({ getService }: FtrProviderContext) { async function fetchEvents(savedObjectType: string, savedObjectId: string) { log.debug(`Fetching events of Saved Object ${savedObjectId}`); return await supertest - .get(`/internal/event_log/${savedObjectType}/${savedObjectId}/_find`) + .get(`/_test/event_log/${savedObjectType}/${savedObjectId}/_find`) .set('kbn-xsrf', 'foo') .expect(200); } diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/background_task_utilization_route.ts b/x-pack/test/plugin_api_integration/test_suites/task_manager/background_task_utilization_route.ts index f58a5d473ca79..9c9dcbbe15126 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/background_task_utilization_route.ts +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/background_task_utilization_route.ts @@ -8,39 +8,11 @@ import expect from '@kbn/expect'; import url from 'url'; import supertest from 'supertest'; +import { MonitoredUtilization } from '@kbn/task-manager-plugin/server/routes/background_task_utilization'; +import { MonitoredStat } from '@kbn/task-manager-plugin/server/monitoring/monitoring_stats_stream'; +import { BackgroundTaskUtilizationStat } from '@kbn/task-manager-plugin/server/monitoring/background_task_utilization_statistics'; import { FtrProviderContext } from '../../ftr_provider_context'; -interface MonitoringStats { - last_update: string; - status: string; - stats: { - timestamp: string; - value: { - adhoc: { - created: { - counter: number; - }; - ran: { - service_time: { - actual: number; - adjusted: number; - task_counter: number; - }; - }; - }; - recurring: { - ran: { - service_time: { - actual: number; - adjusted: number; - task_counter: number; - }; - }; - }; - }; - }; -} - export default function ({ getService }: FtrProviderContext) { const config = getService('config'); const retry = getService('retry'); @@ -48,21 +20,21 @@ export default function ({ getService }: FtrProviderContext) { const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); - function getUtilizationRequest() { + function getUtilizationRequest(isInternal: boolean = true) { return request - .get('/internal/task_manager/_background_task_utilization') + .get(`/${isInternal ? 'internal' : 'api'}/task_manager/_background_task_utilization`) .set('kbn-xsrf', 'foo'); } - function getUtilization(): Promise { - return getUtilizationRequest() + function getUtilization(isInternal: boolean = true): Promise { + return getUtilizationRequest(isInternal) .expect(200) .then((response) => response.body); } - function getBackgroundTaskUtilization(): Promise { + function getBackgroundTaskUtilization(isInternal: boolean = true): Promise { return retry.try(async () => { - const utilization = await getUtilization(); + const utilization = await getUtilization(isInternal); if (utilization.stats) { return utilization; @@ -79,7 +51,8 @@ export default function ({ getService }: FtrProviderContext) { value: { recurring: { ran }, }, - } = (await getBackgroundTaskUtilization()).stats; + } = (await getBackgroundTaskUtilization(true)) + .stats as MonitoredStat; const serviceTime = ran.service_time; expect(typeof serviceTime.actual).to.eql('number'); expect(typeof serviceTime.adjusted).to.eql('number'); @@ -91,7 +64,8 @@ export default function ({ getService }: FtrProviderContext) { value: { adhoc: { created, ran }, }, - } = (await getBackgroundTaskUtilization()).stats; + } = (await getBackgroundTaskUtilization(true)) + .stats as MonitoredStat; const serviceTime = ran.service_time; expect(typeof created.counter).to.eql('number'); @@ -99,5 +73,31 @@ export default function ({ getService }: FtrProviderContext) { expect(typeof serviceTime.adjusted).to.eql('number'); expect(typeof serviceTime.task_counter).to.eql('number'); }); + + it('should include load stat', async () => { + const { + value: { load }, + } = (await getBackgroundTaskUtilization(true)) + .stats as MonitoredStat; + expect(typeof load).to.eql('number'); + }); + + it('should return expected fields for internal route', async () => { + const monitoredStat = (await getBackgroundTaskUtilization(true)).stats; + expect(monitoredStat?.timestamp).not.to.be(undefined); + expect(monitoredStat?.value).not.to.be(undefined); + expect(monitoredStat?.value?.adhoc).not.to.be(undefined); + expect(monitoredStat?.value?.recurring).not.to.be(undefined); + expect(monitoredStat?.value?.load).not.to.be(undefined); + }); + + it('should return expected fields for public route', async () => { + const monitoredStat = (await getBackgroundTaskUtilization(false)).stats; + expect(monitoredStat?.timestamp).not.to.be(undefined); + expect(monitoredStat?.value).not.to.be(undefined); + expect(monitoredStat?.value?.adhoc).to.be(undefined); + expect(monitoredStat?.value?.recurring).to.be(undefined); + expect(monitoredStat?.value?.load).not.to.be(undefined); + }); }); } diff --git a/x-pack/test/rule_registry/common/config.ts b/x-pack/test/rule_registry/common/config.ts index 01d71b6ab8290..703e71a8613b3 100644 --- a/x-pack/test/rule_registry/common/config.ts +++ b/x-pack/test/rule_registry/common/config.ts @@ -5,8 +5,9 @@ * 2.0. */ +import path from 'path'; import { CA_CERT_PATH } from '@kbn/dev-utils'; -import { FtrConfigProviderContext } from '@kbn/test'; +import { FtrConfigProviderContext, findTestPluginPaths } from '@kbn/test'; import { getAllExternalServiceSimulatorPaths } from '@kbn/actions-simulators-plugin/server/plugin'; import { services } from './services'; @@ -83,6 +84,9 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) ...disabledPlugins .filter((k) => k !== 'security') .map((key) => `--xpack.${key}.enabled=false`), + ...findTestPluginPaths([ + path.resolve(__dirname, '../../alerting_api_integration/common/plugins'), + ]), '--xpack.ruleRegistry.write.enabled=true', `--server.xsrf.allowlist=${JSON.stringify(getAllExternalServiceSimulatorPaths())}`, ...(ssl diff --git a/x-pack/test/spaces_api_integration/common/config.ts b/x-pack/test/spaces_api_integration/common/config.ts index da29de4033eb8..3e0cd4b0a38bb 100644 --- a/x-pack/test/spaces_api_integration/common/config.ts +++ b/x-pack/test/spaces_api_integration/common/config.ts @@ -13,10 +13,11 @@ import { FtrConfigProviderContext } from '@kbn/test'; interface CreateTestConfigOptions { license: string; disabledPlugins?: string[]; + testFiles?: string[]; } export function createTestConfig(name: string, options: CreateTestConfigOptions) { - const { license, disabledPlugins = [] } = options; + const { license, disabledPlugins = [], testFiles } = options; return async ({ readConfigFile }: FtrConfigProviderContext) => { const config = { @@ -32,7 +33,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) }; return { - testFiles: [require.resolve(`../${name}/apis/`)], + testFiles: testFiles ?? [require.resolve(`../${name}/apis/`)], servers: config.xpack.api.get('servers'), services: { es: config.kibana.api.get('services.es'), diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/apis/copy_to_space.ts b/x-pack/test/spaces_api_integration/security_and_spaces/apis/copy_to_space/copy_to_space.ts similarity index 96% rename from x-pack/test/spaces_api_integration/security_and_spaces/apis/copy_to_space.ts rename to x-pack/test/spaces_api_integration/security_and_spaces/apis/copy_to_space/copy_to_space.ts index 2a99ae8afceb6..90b2cc67439d7 100644 --- a/x-pack/test/spaces_api_integration/security_and_spaces/apis/copy_to_space.ts +++ b/x-pack/test/spaces_api_integration/security_and_spaces/apis/copy_to_space/copy_to_space.ts @@ -5,10 +5,10 @@ * 2.0. */ -import { AUTHENTICATION } from '../../common/lib/authentication'; -import { SPACES } from '../../common/lib/spaces'; -import { copyToSpaceTestSuiteFactory } from '../../common/suites/copy_to_space'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { AUTHENTICATION } from '../../../common/lib/authentication'; +import { SPACES } from '../../../common/lib/spaces'; +import { copyToSpaceTestSuiteFactory } from '../../../common/suites/copy_to_space'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function copyToSpaceSpacesAndSecuritySuite(context: FtrProviderContext) { diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/apis/copy_to_space/index.ts b/x-pack/test/spaces_api_integration/security_and_spaces/apis/copy_to_space/index.ts new file mode 100644 index 0000000000000..c1edb1e5e5ac1 --- /dev/null +++ b/x-pack/test/spaces_api_integration/security_and_spaces/apis/copy_to_space/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createUsersAndRoles } from '../../../common/lib/create_users_and_roles'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function ({ loadTestFile, getService }: FtrProviderContext) { + const es = getService('es'); + const supertest = getService('supertest'); + + describe('copy to space with security', function () { + before(async () => { + await createUsersAndRoles(es, supertest); + }); + + loadTestFile(require.resolve('./copy_to_space')); // ~ 19m 20s + }); +} diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts b/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts index 75381f35dacd3..ae5f7b48a2809 100644 --- a/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts +++ b/x-pack/test/spaces_api_integration/security_and_spaces/apis/index.ts @@ -18,15 +18,15 @@ export default function ({ loadTestFile, getService }: FtrProviderContext) { await createUsersAndRoles(es, supertest); }); - loadTestFile(require.resolve('./copy_to_space')); - loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); - loadTestFile(require.resolve('./create')); - loadTestFile(require.resolve('./delete')); - loadTestFile(require.resolve('./get_all')); - loadTestFile(require.resolve('./get_shareable_references')); - loadTestFile(require.resolve('./get')); - loadTestFile(require.resolve('./update')); - loadTestFile(require.resolve('./update_objects_spaces')); - loadTestFile(require.resolve('./disable_legacy_url_aliases')); + // total runtime ~ 17m + loadTestFile(require.resolve('./resolve_copy_to_space_conflicts')); // ~ 10m + loadTestFile(require.resolve('./create')); // ~ 2m + loadTestFile(require.resolve('./delete')); // ~ 1m 20s + loadTestFile(require.resolve('./get_all')); // ~ 50s + loadTestFile(require.resolve('./get_shareable_references')); // ~ 30s + loadTestFile(require.resolve('./get')); // ~ 30s + loadTestFile(require.resolve('./update')); // ~ 30s + loadTestFile(require.resolve('./update_objects_spaces')); // ~ 1m + loadTestFile(require.resolve('./disable_legacy_url_aliases')); // ~ 30s }); } diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/copy_to_space_config_basic.ts b/x-pack/test/spaces_api_integration/security_and_spaces/copy_to_space_config_basic.ts new file mode 100644 index 0000000000000..609d747fa8861 --- /dev/null +++ b/x-pack/test/spaces_api_integration/security_and_spaces/copy_to_space_config_basic.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 { createTestConfig } from '../common/config'; + +// eslint-disable-next-line import/no-default-export +export default createTestConfig('security_and_spaces', { + license: 'basic', + testFiles: [require.resolve('./apis/copy_to_space')], +}); diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/copy_to_space_config_trial.ts b/x-pack/test/spaces_api_integration/security_and_spaces/copy_to_space_config_trial.ts new file mode 100644 index 0000000000000..da8834134f258 --- /dev/null +++ b/x-pack/test/spaces_api_integration/security_and_spaces/copy_to_space_config_trial.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 { createTestConfig } from '../common/config'; + +// eslint-disable-next-line import/no-default-export +export default createTestConfig('security_and_spaces', { + license: 'trial', + testFiles: [require.resolve('./apis/copy_to_space')], +}); diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index dc89799511927..91a8e991fcdda 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -124,5 +124,6 @@ "@kbn/assetManager-plugin", "@kbn/guided-onboarding-plugin", "@kbn/field-formats-plugin", + "@kbn/ml-anomaly-utils", ] } diff --git a/yarn.lock b/yarn.lock index e42d31edfada7..5aa75006e8c02 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4562,6 +4562,10 @@ version "0.0.0" uid "" +"@kbn/ml-anomaly-utils@link:x-pack/packages/ml/anomaly_utils": + version "0.0.0" + uid "" + "@kbn/ml-date-picker@link:x-pack/packages/ml/date_picker": version "0.0.0" uid "" @@ -5042,6 +5046,10 @@ version "0.0.0" uid "" +"@kbn/serverless-security@link:x-pack/plugins/serverless_security": + version "0.0.0" + uid "" + "@kbn/serverless@link:x-pack/plugins/serverless": version "0.0.0" uid "" @@ -12365,7 +12373,7 @@ commander@^4.0.1, commander@^4.1.1: resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== -commander@^5.0.0, commander@^5.1.0: +commander@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== @@ -13076,23 +13084,23 @@ cyclist@~0.2.2: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" integrity sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA= -cypress-axe@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/cypress-axe/-/cypress-axe-1.3.0.tgz#255ef8ef8e88747f2a72ceb7f7c60e8185b7852b" - integrity sha512-b2zAva1+uRwGA7r/JzP7C/64YHu9Fa8RsHRIrapUDzJeGLEQImz86FbwRW/lBamrEt7YHzGRwuJizXKTyQBsfQ== +cypress-axe@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/cypress-axe/-/cypress-axe-1.4.0.tgz#e67482bfe9e740796bf77c7823f19781a8a2faff" + integrity sha512-Ut7NKfzjyKm0BEbt2WxuKtLkIXmx6FD2j0RwdvO/Ykl7GmB/qRQkwbKLk3VP35+83hiIr8GKD04PDdrTK5BnyA== cypress-file-upload@^5.0.8: version "5.0.8" resolved "https://registry.yarnpkg.com/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz#d8824cbeaab798e44be8009769f9a6c9daa1b4a1" integrity sha512-+8VzNabRk3zG6x8f8BWArF/xA/W0VK4IZNx3MV0jFWrJS/qKn8eHfa5nU73P9fOQAgwHFJx7zjg4lwOnljMO8g== -cypress-multi-reporters@^1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/cypress-multi-reporters/-/cypress-multi-reporters-1.6.2.tgz#129dfeffa00d4deca3e9f58d84570b9962c28c2b" - integrity sha512-lvwGwHqZG5CwGxBJ6UJXWaxlWGkJgxBjP0h+IVLrrwRlJpT4coSwwt+UzMdeqEMrzT4IDfhbtmUNOiDleisOYA== +cypress-multi-reporters@^1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/cypress-multi-reporters/-/cypress-multi-reporters-1.6.3.tgz#0f0da8db4caf8d7a21f94e5209148348416d7c71" + integrity sha512-klb9pf6oAF4WCLHotu9gdB8ukYBdeTzbEMuESKB3KT54HhrZj65vQxubAgrULV5H2NWqxHdUhlntPbKZChNvEw== dependencies: - debug "^4.1.1" - lodash "^4.17.15" + debug "^4.3.4" + lodash "^4.17.21" cypress-pipe@^2.0.0: version "2.0.0" @@ -13111,17 +13119,17 @@ cypress-real-events@^1.7.6: resolved "https://registry.yarnpkg.com/cypress-real-events/-/cypress-real-events-1.7.6.tgz#6f17e0b2ceea1d6dc60f6737d8f84cc517bbbb4c" integrity sha512-yP6GnRrbm6HK5q4DH6Nnupz37nOfZu/xn1xFYqsE2o4G73giPWQOdu6375QYpwfU1cvHNCgyD2bQ2hPH9D7NMw== -cypress-recurse@^1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/cypress-recurse/-/cypress-recurse-1.27.0.tgz#0c61e809c5f7740a7e907714614c49c72dcb5c1f" - integrity sha512-BCD83UqaxlD+JiqZn1PvIhHRXasgfCt57vLC1Fcyifvxh4QklELRcYUJV3MdhKamMkmajaErLfnCNbZ8VJ5SIg== +cypress-recurse@^1.31.2: + version "1.31.2" + resolved "https://registry.yarnpkg.com/cypress-recurse/-/cypress-recurse-1.31.2.tgz#c926b28207c9d49acb1caf89b818cb4910af43eb" + integrity sha512-McSjAaL95bq6NSg0vLA0GI5rMShqKmNlCQXpuQcAga5n2Yf/t3NgELBjPk1mXE0RfpLwymVFjiFQgmxkBgwK7A== dependencies: humanize-duration "^3.27.3" -cypress@^12.6.0: - version "12.6.0" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.6.0.tgz#d71a82639756173c0682b3d467eb9f0523460e91" - integrity sha512-WdHSVaS1lumSd5XpVTslZd8ui9GIGphrzvXq9+3DtVhqjRZC5M70gu5SW/Y/SLPq3D1wiXGZoHC6HJ7ESVE2lw== +cypress@^12.10.0: + version "12.10.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.10.0.tgz#b6264f77c214d63530ebac2b33c4d099bd40b715" + integrity sha512-Y0wPc221xKKW1/4iAFCphkrG2jNR4MjOne3iGn4mcuCaE7Y5EtXL83N8BzRsAht7GYfWVjJ/UeTqEdDKHz39HQ== dependencies: "@cypress/request" "^2.88.10" "@cypress/xvfb" "^1.2.4" @@ -13137,7 +13145,7 @@ cypress@^12.6.0: check-more-types "^2.24.0" cli-cursor "^3.1.0" cli-table3 "~0.6.1" - commander "^5.1.0" + commander "^6.2.1" common-tags "^1.8.0" dayjs "^1.10.4" debug "^4.3.4" @@ -13155,7 +13163,7 @@ cypress@^12.6.0: listr2 "^3.8.3" lodash "^4.17.21" log-symbols "^4.0.0" - minimist "^1.2.6" + minimist "^1.2.8" ospath "^1.2.2" pretty-bytes "^5.6.0" proxy-from-env "1.0.0" @@ -15022,10 +15030,10 @@ eslint-plugin-ban@^1.5.2: dependencies: requireindex "~1.2.0" -eslint-plugin-cypress@^2.12.1: - version "2.12.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-cypress/-/eslint-plugin-cypress-2.12.1.tgz#9aeee700708ca8c058e00cdafe215199918c2632" - integrity sha512-c2W/uPADl5kospNDihgiLc7n87t5XhUbFDoTl6CfVkmG+kDAb5Ux10V9PoLPu9N+r7znpc+iQlcmAqT1A/89HA== +eslint-plugin-cypress@^2.13.2: + version "2.13.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-cypress/-/eslint-plugin-cypress-2.13.2.tgz#b42b763f449ff713cecf6bdf1903e7cee6e48bfc" + integrity sha512-LlwjnBTzuKuC0A4H0RxVjs0YeAWK+CD1iM9Dp8un3lzT713ePQxfpPstCD+9HSAss8emuE3b2hCNUST+NrUwKw== dependencies: globals "^11.12.0" @@ -21122,6 +21130,11 @@ minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.3, minimist@^1. resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== +minimist@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + minipass-collect@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617"