diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index d8fa6967bd583..cec7df55fbc00 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github description: API docs for the actions plugin -date: 2023-04-05 +date: 2023-04-06 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 0641ca6a129a3..afea2f12a9de7 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github description: API docs for the advancedSettings plugin -date: 2023-04-05 +date: 2023-04-06 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 ca8c859edabf3..8f38223822de5 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github description: API docs for the aiops plugin -date: 2023-04-05 +date: 2023-04-06 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 daae701a9682a..0ee9a8786a4b3 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github description: API docs for the alerting plugin -date: 2023-04-05 +date: 2023-04-06 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 8165755a41f2f..3ee721dc9506d 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github description: API docs for the apm plugin -date: 2023-04-05 +date: 2023-04-06 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 f99bf7ac6b57d..c3d71a94756fd 100644 --- a/api_docs/asset_manager.mdx +++ b/api_docs/asset_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/assetManager title: "assetManager" image: https://source.unsplash.com/400x175/?github description: API docs for the assetManager plugin -date: 2023-04-05 +date: 2023-04-06 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 17aa8009f9adf..9092d473f5a9e 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github description: API docs for the banners plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners'] --- import bannersObj from './banners.devdocs.json'; diff --git a/api_docs/bfetch.devdocs.json b/api_docs/bfetch.devdocs.json index e4920b57c5d7b..a8de64ac0bb06 100644 --- a/api_docs/bfetch.devdocs.json +++ b/api_docs/bfetch.devdocs.json @@ -846,7 +846,7 @@ "tags": [], "label": "flush", "description": [ - "\nCall `.onflush` method and clear buffer." + "\nCall `.onFlush` method and clear buffer." ], "signature": [ "() => void" @@ -856,6 +856,24 @@ "trackAdoption": false, "children": [], "returnComment": [] + }, + { + "parentPluginId": "bfetch", + "id": "def-common.ItemBuffer.flushAsync", + "type": "Function", + "tags": [], + "label": "flushAsync", + "description": [ + "\nSame as `.flush()` but asynchronous, and returns a promise, which\nrejects if `.onFlush` throws." + ], + "signature": [ + "() => Promise" + ], + "path": "src/plugins/bfetch/common/buffer/item_buffer.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] } ], "initialIsOpen": false @@ -991,6 +1009,22 @@ "trackAdoption": false, "children": [], "returnComment": [] + }, + { + "parentPluginId": "bfetch", + "id": "def-common.TimedItemBuffer.flushAsync", + "type": "Function", + "tags": [], + "label": "flushAsync", + "description": [], + "signature": [ + "() => Promise" + ], + "path": "src/plugins/bfetch/common/buffer/timed_item_buffer.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] } ], "initialIsOpen": false @@ -1525,7 +1559,7 @@ "\nCallback that is called every time buffer is flushed. It receives a single\nargument which is a list of all buffered items. If `.flush()` is called\nwhen buffer is empty, `.onflush` is called with empty array." ], "signature": [ - "(items: Item[]) => void" + "(items: Item[]) => void | Promise" ], "path": "src/plugins/bfetch/common/buffer/item_buffer.ts", "deprecated": false, diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx index 265b31f1296d1..b1144758e8463 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github description: API docs for the bfetch plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'bfetch'] --- import bfetchObj from './bfetch.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 | |-------------------|-----------|------------------------|-----------------| -| 89 | 1 | 74 | 2 | +| 91 | 1 | 75 | 2 | ## Client diff --git a/api_docs/canvas.mdx b/api_docs/canvas.mdx index 61f7d45dbb718..4c7e7b899449f 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github description: API docs for the canvas plugin -date: 2023-04-05 +date: 2023-04-06 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 626c1f7e6f605..81d40c1f7432b 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github description: API docs for the cases plugin -date: 2023-04-05 +date: 2023-04-06 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 9c471977eebcf..6fa17872bb7cc 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github description: API docs for the charts plugin -date: 2023-04-05 +date: 2023-04-06 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 290d1116df312..942d1a2f73aef 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github description: API docs for the cloud plugin -date: 2023-04-05 +date: 2023-04-06 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 10431d522ae41..58b3e0dff0c32 100644 --- a/api_docs/cloud_chat.mdx +++ b/api_docs/cloud_chat.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudChat title: "cloudChat" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudChat plugin -date: 2023-04-05 +date: 2023-04-06 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 f0097f5280914..63cff73808fa5 100644 --- a/api_docs/cloud_data_migration.mdx +++ b/api_docs/cloud_data_migration.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDataMigration title: "cloudDataMigration" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDataMigration plugin -date: 2023-04-05 +date: 2023-04-06 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 1d1e0b71eed16..ef50fc6b35fc5 100644 --- a/api_docs/cloud_defend.mdx +++ b/api_docs/cloud_defend.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDefend title: "cloudDefend" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDefend plugin -date: 2023-04-05 +date: 2023-04-06 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 0baaa355ebe86..ee59dd29109d3 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2023-04-05 +date: 2023-04-06 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 a20813f94e96c..a1499811e304c 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudSecurityPosture plugin -date: 2023-04-05 +date: 2023-04-06 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 8d2e6e0b6ea1a..22674adedc895 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github description: API docs for the console plugin -date: 2023-04-05 +date: 2023-04-06 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 6bcd0fe5940b3..9c6d31d79f9ac 100644 --- a/api_docs/content_management.mdx +++ b/api_docs/content_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/contentManagement title: "contentManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the contentManagement plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'contentManagement'] --- import contentManagementObj from './content_management.devdocs.json'; diff --git a/api_docs/controls.devdocs.json b/api_docs/controls.devdocs.json index 9b8b0ed06703b..ad5df876690bd 100644 --- a/api_docs/controls.devdocs.json +++ b/api_docs/controls.devdocs.json @@ -2190,9 +2190,15 @@ "label": "isFieldCompatible", "description": [], "signature": [ - "(dataControlField: ", - "DataControlField", - ") => void" + "(field: ", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewField", + "text": "DataViewField" + }, + ") => boolean" ], "path": "src/plugins/controls/public/options_list/embeddable/options_list_embeddable_factory.tsx", "deprecated": false, @@ -2203,10 +2209,16 @@ "id": "def-public.OptionsListEmbeddableFactory.isFieldCompatible.$1", "type": "Object", "tags": [], - "label": "dataControlField", + "label": "field", "description": [], "signature": [ - "DataControlField" + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewField", + "text": "DataViewField" + } ], "path": "src/plugins/controls/public/options_list/embeddable/options_list_embeddable_factory.tsx", "deprecated": false, @@ -3164,9 +3176,15 @@ "label": "isFieldCompatible", "description": [], "signature": [ - "(dataControlField: ", - "DataControlField", - ") => void" + "(field: ", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewField", + "text": "DataViewField" + }, + ") => boolean" ], "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx", "deprecated": false, @@ -3177,10 +3195,16 @@ "id": "def-public.RangeSliderEmbeddableFactory.isFieldCompatible.$1", "type": "Object", "tags": [], - "label": "dataControlField", + "label": "field", "description": [], "signature": [ - "DataControlField" + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewField", + "text": "DataViewField" + } ], "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx", "deprecated": false, @@ -4110,7 +4134,55 @@ "section": "def-public.IEditableControlFactory", "text": "IEditableControlFactory" }, - "" + " extends Pick<", + { + "pluginId": "embeddable", + "scope": "public", + "docId": "kibEmbeddablePluginApi", + "section": "def-public.EmbeddableFactory", + "text": "EmbeddableFactory" + }, + "<", + { + "pluginId": "embeddable", + "scope": "common", + "docId": "kibEmbeddablePluginApi", + "section": "def-common.EmbeddableInput", + "text": "EmbeddableInput" + }, + ", ", + { + "pluginId": "embeddable", + "scope": "public", + "docId": "kibEmbeddablePluginApi", + "section": "def-public.EmbeddableOutput", + "text": "EmbeddableOutput" + }, + ", ", + { + "pluginId": "embeddable", + "scope": "public", + "docId": "kibEmbeddablePluginApi", + "section": "def-public.IEmbeddable", + "text": "IEmbeddable" + }, + "<", + { + "pluginId": "embeddable", + "scope": "common", + "docId": "kibEmbeddablePluginApi", + "section": "def-common.EmbeddableInput", + "text": "EmbeddableInput" + }, + ", ", + { + "pluginId": "embeddable", + "scope": "public", + "docId": "kibEmbeddablePluginApi", + "section": "def-public.EmbeddableOutput", + "text": "EmbeddableOutput" + }, + ", any>, unknown>, \"type\">" ], "path": "src/plugins/controls/public/types.ts", "deprecated": false, @@ -4249,9 +4321,15 @@ "label": "isFieldCompatible", "description": [], "signature": [ - "((dataControlField: ", - "DataControlField", - ") => void) | undefined" + "((field: ", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewField", + "text": "DataViewField" + }, + ") => boolean) | undefined" ], "path": "src/plugins/controls/public/types.ts", "deprecated": false, @@ -4262,10 +4340,16 @@ "id": "def-public.IEditableControlFactory.isFieldCompatible.$1", "type": "Object", "tags": [], - "label": "dataControlField", + "label": "field", "description": [], "signature": [ - "DataControlField" + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewField", + "text": "DataViewField" + } ], "path": "src/plugins/controls/public/types.ts", "deprecated": false, diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index 484d3b5352660..3d791bd5032d1 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github description: API docs for the controls plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kib | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 273 | 0 | 269 | 11 | +| 273 | 0 | 269 | 10 | ## Client diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index db8839ee38fac..37dc66e581e8c 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github description: API docs for the customIntegrations plugin -date: 2023-04-05 +date: 2023-04-06 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 bbb62384d2bc3..af0cca806e1f1 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboard plugin -date: 2023-04-05 +date: 2023-04-06 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 cc4baef6a7729..91db924d2a5ae 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboardEnhanced plugin -date: 2023-04-05 +date: 2023-04-06 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 d081674dc20d7..1209eb81e4ccf 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github description: API docs for the data plugin -date: 2023-04-05 +date: 2023-04-06 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 5412b5ebf5ebf..9b9b921092a4d 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github description: API docs for the data.query plugin -date: 2023-04-05 +date: 2023-04-06 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 ebd19a80dfbb2..962a9c639fe48 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github description: API docs for the data.search plugin -date: 2023-04-05 +date: 2023-04-06 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 06deb8c6472fa..4eb1e77a77f2b 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewEditor plugin -date: 2023-04-05 +date: 2023-04-06 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 3656d4fe70c1e..91f10192b8f0d 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewFieldEditor plugin -date: 2023-04-05 +date: 2023-04-06 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 b4bd565aa1d6b..aa0c232fc281d 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewManagement plugin -date: 2023-04-05 +date: 2023-04-06 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 1f0d00ee4b9cd..b6d38dace1f6b 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViews plugin -date: 2023-04-05 +date: 2023-04-06 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 ef28833000a62..8a94aea692c51 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github description: API docs for the dataVisualizer plugin -date: 2023-04-05 +date: 2023-04-06 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 8261c90fdfa0f..fe96fc224c26a 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index a9994d6b8c37f..bc45a0a42d24d 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 77be56ed03244..5f7a13a840a76 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team description: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 747df0b9d2d43..fb54b84003726 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github description: API docs for the devTools plugin -date: 2023-04-05 +date: 2023-04-06 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 548e662f86726..a6fe824860fb3 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github description: API docs for the discover plugin -date: 2023-04-05 +date: 2023-04-06 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 9f18ea27ca451..5c806e637f16d 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverEnhanced plugin -date: 2023-04-05 +date: 2023-04-06 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 825e495ff0498..beeb864f99fbc 100644 --- a/api_docs/ecs_data_quality_dashboard.mdx +++ b/api_docs/ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ecsDataQualityDashboard title: "ecsDataQualityDashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the ecsDataQualityDashboard plugin -date: 2023-04-05 +date: 2023-04-06 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 aca2b0b4a68f1..aa4f941d60b9c 100644 --- a/api_docs/embeddable.mdx +++ b/api_docs/embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddable title: "embeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddable plugin -date: 2023-04-05 +date: 2023-04-06 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 28f7c59e43d77..752c8c3540fbf 100644 --- a/api_docs/embeddable_enhanced.mdx +++ b/api_docs/embeddable_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced title: "embeddableEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddableEnhanced plugin -date: 2023-04-05 +date: 2023-04-06 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 f096dc2ea120c..a581bd2b98ac1 100644 --- a/api_docs/encrypted_saved_objects.mdx +++ b/api_docs/encrypted_saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects title: "encryptedSavedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the encryptedSavedObjects plugin -date: 2023-04-05 +date: 2023-04-06 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 0cfdc87d713d6..0a325e4a1ab94 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the enterpriseSearch plugin -date: 2023-04-05 +date: 2023-04-06 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 94708fca87670..be1adb5ff8e62 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github description: API docs for the esUiShared plugin -date: 2023-04-05 +date: 2023-04-06 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 1b3e70b277a41..ccf41dc4a8f2d 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotation plugin -date: 2023-04-05 +date: 2023-04-06 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 3e547519d20e6..23bdd25a9bdea 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github description: API docs for the eventLog plugin -date: 2023-04-05 +date: 2023-04-06 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 61ed0e398b342..da8880e48fe50 100644 --- a/api_docs/exploratory_view.mdx +++ b/api_docs/exploratory_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/exploratoryView title: "exploratoryView" image: https://source.unsplash.com/400x175/?github description: API docs for the exploratoryView plugin -date: 2023-04-05 +date: 2023-04-06 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 eabb4ca7f9b19..bc022b8caf756 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionError plugin -date: 2023-04-05 +date: 2023-04-06 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 df0a71e6c809b..15cae19f19d30 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionGauge plugin -date: 2023-04-05 +date: 2023-04-06 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 a023146e63f60..13132b7ec276f 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionHeatmap plugin -date: 2023-04-05 +date: 2023-04-06 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 7ded2eee07d62..01ff6f377cadf 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionImage plugin -date: 2023-04-05 +date: 2023-04-06 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 0b0a66c06ee82..783798833296a 100644 --- a/api_docs/expression_legacy_metric_vis.mdx +++ b/api_docs/expression_legacy_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionLegacyMetricVis title: "expressionLegacyMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionLegacyMetricVis plugin -date: 2023-04-05 +date: 2023-04-06 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 b0f2338b3b817..406f47f3fe71e 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetric plugin -date: 2023-04-05 +date: 2023-04-06 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 bd06200bdf936..8330f4402829e 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetricVis plugin -date: 2023-04-05 +date: 2023-04-06 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 6c9169cc9d670..2807b25b40ba5 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionPartitionVis plugin -date: 2023-04-05 +date: 2023-04-06 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 0780e138dbb28..44749553d796f 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRepeatImage plugin -date: 2023-04-05 +date: 2023-04-06 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 270d570ab1799..c84973bb7a4b6 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRevealImage plugin -date: 2023-04-05 +date: 2023-04-06 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 df90eae281c6a..93a802cefd7dc 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionShape plugin -date: 2023-04-05 +date: 2023-04-06 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 7fc642a26a8ee..5e90b81613832 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionTagcloud plugin -date: 2023-04-05 +date: 2023-04-06 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 d89b0b7e5b307..02521068f95dd 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionXY plugin -date: 2023-04-05 +date: 2023-04-06 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 a19214e4336b3..c24c468f6e053 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github description: API docs for the expressions plugin -date: 2023-04-05 +date: 2023-04-06 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 79a661619df71..af12cce6e713c 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github description: API docs for the features plugin -date: 2023-04-05 +date: 2023-04-06 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 e571f804f656c..3a62ec1414aba 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldFormats plugin -date: 2023-04-05 +date: 2023-04-06 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 2104820265a6a..d2e3c4f7e88f6 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github description: API docs for the fileUpload plugin -date: 2023-04-05 +date: 2023-04-06 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 f381e571ee648..fbeacf3e59bbf 100644 --- a/api_docs/files.mdx +++ b/api_docs/files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/files title: "files" image: https://source.unsplash.com/400x175/?github description: API docs for the files plugin -date: 2023-04-05 +date: 2023-04-06 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 f6c989b7ae391..10c349340c469 100644 --- a/api_docs/files_management.mdx +++ b/api_docs/files_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/filesManagement title: "filesManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the filesManagement plugin -date: 2023-04-05 +date: 2023-04-06 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 d7b82f129211c..325c7b066f299 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the fleet plugin -date: 2023-04-05 +date: 2023-04-06 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 eb237f61e76a9..6278a07fdc62a 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the globalSearch plugin -date: 2023-04-05 +date: 2023-04-06 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 a667713a9d6fe..7fce8f60005e4 100644 --- a/api_docs/guided_onboarding.mdx +++ b/api_docs/guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/guidedOnboarding title: "guidedOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the guidedOnboarding plugin -date: 2023-04-05 +date: 2023-04-06 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 4be478fb2366d..396095c3caf79 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github description: API docs for the home plugin -date: 2023-04-05 +date: 2023-04-06 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 b0d909214d183..1385d0d5d267a 100644 --- a/api_docs/image_embeddable.mdx +++ b/api_docs/image_embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/imageEmbeddable title: "imageEmbeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the imageEmbeddable plugin -date: 2023-04-05 +date: 2023-04-06 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 ff488c6db57d9..fa4cb8731e1a1 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexLifecycleManagement plugin -date: 2023-04-05 +date: 2023-04-06 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 e489b9d398709..8ea4e85d29055 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexManagement plugin -date: 2023-04-05 +date: 2023-04-06 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 dce129ec77260..0ad6e1ba5029e 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github description: API docs for the infra plugin -date: 2023-04-05 +date: 2023-04-06 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 d36655fea8fb9..b49638258e21b 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github description: API docs for the inspector plugin -date: 2023-04-05 +date: 2023-04-06 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 fb253b30accb1..73c3a5fc2f7e0 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github description: API docs for the interactiveSetup plugin -date: 2023-04-05 +date: 2023-04-06 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 326f5e55b5d41..45afe644abc71 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2023-04-05 +date: 2023-04-06 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 c1af4d0422ab5..9ed71e3f3a44f 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-components plugin -date: 2023-04-05 +date: 2023-04-06 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 3f34957bed484..96a1bbe151c5b 100644 --- a/api_docs/kbn_aiops_utils.mdx +++ b/api_docs/kbn_aiops_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils title: "@kbn/aiops-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-utils plugin -date: 2023-04-05 +date: 2023-04-06 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 c45be7558e553..f8c4b66b04b66 100644 --- a/api_docs/kbn_alerting_state_types.mdx +++ b/api_docs/kbn_alerting_state_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-state-types title: "@kbn/alerting-state-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-state-types plugin -date: 2023-04-05 +date: 2023-04-06 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 98d6d98dd9ca6..e02feb1cc1ae4 100644 --- a/api_docs/kbn_alerts.mdx +++ b/api_docs/kbn_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts title: "@kbn/alerts" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts plugin -date: 2023-04-05 +date: 2023-04-06 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 02b954d878772..c616b1a37b5da 100644 --- a/api_docs/kbn_alerts_as_data_utils.mdx +++ b/api_docs/kbn_alerts_as_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-as-data-utils title: "@kbn/alerts-as-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-as-data-utils plugin -date: 2023-04-05 +date: 2023-04-06 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 dcef16c3f5685..cff792cd3a8ac 100644 --- a/api_docs/kbn_alerts_ui_shared.mdx +++ b/api_docs/kbn_alerts_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-ui-shared title: "@kbn/alerts-ui-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-ui-shared plugin -date: 2023-04-05 +date: 2023-04-06 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 a780e79292053..d552186fd6a6a 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics plugin -date: 2023-04-05 +date: 2023-04-06 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 554fa513f7aeb..20e5d7cf8e8dc 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2023-04-05 +date: 2023-04-06 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 82dd55fb40227..c3ca34185b4b9 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2023-04-05 +date: 2023-04-06 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 820e1c1d6c12a..1ca442639427c 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2023-04-05 +date: 2023-04-06 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 3c43d37079c95..b99d3c74dd266 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2023-04-05 +date: 2023-04-06 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 8c4ee2d25534e..fedd04f15c406 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2023-04-05 +date: 2023-04-06 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 e5deb342baf29..0f895aae6d5a3 100644 --- a/api_docs/kbn_analytics_shippers_gainsight.mdx +++ b/api_docs/kbn_analytics_shippers_gainsight.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-gainsight title: "@kbn/analytics-shippers-gainsight" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-gainsight plugin -date: 2023-04-05 +date: 2023-04-06 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 207e0abb82307..3416d8b5ae190 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-config-loader plugin -date: 2023-04-05 +date: 2023-04-06 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 f7820b2eb497c..e60ea5e71c5dc 100644 --- a/api_docs/kbn_apm_synthtrace.mdx +++ b/api_docs/kbn_apm_synthtrace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace title: "@kbn/apm-synthtrace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace plugin -date: 2023-04-05 +date: 2023-04-06 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 9a57b71e70af6..85f247ff59e5e 100644 --- a/api_docs/kbn_apm_synthtrace_client.mdx +++ b/api_docs/kbn_apm_synthtrace_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace-client title: "@kbn/apm-synthtrace-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace-client plugin -date: 2023-04-05 +date: 2023-04-06 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 3c28c83d26eca..d57de97297cc6 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-utils plugin -date: 2023-04-05 +date: 2023-04-06 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 5ca82e15cedb0..eff314a0278a0 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/axe-config plugin -date: 2023-04-05 +date: 2023-04-06 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 68e3a9c7c129c..d78bf2e26796f 100644 --- a/api_docs/kbn_cases_components.mdx +++ b/api_docs/kbn_cases_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cases-components title: "@kbn/cases-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cases-components plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cases-components'] --- import kbnCasesComponentsObj from './kbn_cases_components.devdocs.json'; diff --git a/api_docs/kbn_cell_actions.mdx b/api_docs/kbn_cell_actions.mdx index ea528b5f788cd..93876c974fa0d 100644 --- a/api_docs/kbn_cell_actions.mdx +++ b/api_docs/kbn_cell_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cell-actions title: "@kbn/cell-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cell-actions plugin -date: 2023-04-05 +date: 2023-04-06 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 5521b383f350a..2165d6310075b 100644 --- a/api_docs/kbn_chart_expressions_common.mdx +++ b/api_docs/kbn_chart_expressions_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-expressions-common title: "@kbn/chart-expressions-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-expressions-common plugin -date: 2023-04-05 +date: 2023-04-06 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 2c14fa4fe0fc3..1056f449ce804 100644 --- a/api_docs/kbn_chart_icons.mdx +++ b/api_docs/kbn_chart_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-icons title: "@kbn/chart-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-icons plugin -date: 2023-04-05 +date: 2023-04-06 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 7b227d64d03a0..2d4413dee79c9 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-core plugin -date: 2023-04-05 +date: 2023-04-06 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 638383aa47a9d..5414755f48a06 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2023-04-05 +date: 2023-04-06 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 7958f562872f6..1b29d1e4e9dda 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-reporter plugin -date: 2023-04-05 +date: 2023-04-06 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 50e5e35ea010e..8eb61a89a971b 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cli-dev-mode plugin -date: 2023-04-05 +date: 2023-04-06 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 c2ebb29f0c33e..12ea2ecb4d4bd 100644 --- a/api_docs/kbn_code_editor.mdx +++ b/api_docs/kbn_code_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor title: "@kbn/code-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor plugin -date: 2023-04-05 +date: 2023-04-06 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 5b275844d524a..6d47af2078a50 100644 --- a/api_docs/kbn_code_editor_mocks.mdx +++ b/api_docs/kbn_code_editor_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor-mocks title: "@kbn/code-editor-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 e7f80ae52c956..5d2e797728738 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/coloring plugin -date: 2023-04-05 +date: 2023-04-06 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 aafeda5ad5beb..a05f2f1470e38 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config plugin -date: 2023-04-05 +date: 2023-04-06 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 c7a5ef5e6806d..41ed159aa027f 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 ee17a18e822fe..ff1fb6dfe0e8c 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-schema plugin -date: 2023-04-05 +date: 2023-04-06 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 1d509a8d89b83..7e279a5a366f8 100644 --- a/api_docs/kbn_content_management_content_editor.mdx +++ b/api_docs/kbn_content_management_content_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-editor title: "@kbn/content-management-content-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-editor plugin -date: 2023-04-05 +date: 2023-04-06 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 283af3367b159..83ff790f0ee76 100644 --- a/api_docs/kbn_content_management_table_list.mdx +++ b/api_docs/kbn_content_management_table_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list title: "@kbn/content-management-table-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list'] --- import kbnContentManagementTableListObj from './kbn_content_management_table_list.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index c6cb8b98738b0..faa5f1c5a60e1 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser plugin -date: 2023-04-05 +date: 2023-04-06 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 ae90e97b27da7..990db0f0e9166 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 5a8a1274afe58..d427176287801 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 cd7f89cca4e37..248fffdcf0b2d 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server plugin -date: 2023-04-05 +date: 2023-04-06 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 ab0327b442abd..67bc49398b389 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 c88ea7cf2c7e8..e81d98412ca99 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 4b91920d0ffc1..0bb51f3b6b847 100644 --- a/api_docs/kbn_core_application_browser.mdx +++ b/api_docs/kbn_core_application_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser title: "@kbn/core-application-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser plugin -date: 2023-04-05 +date: 2023-04-06 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 f989d368e6c46..46a622c1c79e2 100644 --- a/api_docs/kbn_core_application_browser_internal.mdx +++ b/api_docs/kbn_core_application_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-internal title: "@kbn/core-application-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 ae0eeec002b25..a2e51e26d3eee 100644 --- a/api_docs/kbn_core_application_browser_mocks.mdx +++ b/api_docs/kbn_core_application_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-mocks title: "@kbn/core-application-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 aa19b2ef0cfef..dc7ef76af7bb4 100644 --- a/api_docs/kbn_core_application_common.mdx +++ b/api_docs/kbn_core_application_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-common title: "@kbn/core-application-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-common plugin -date: 2023-04-05 +date: 2023-04-06 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 acb93140d0f56..ae42642c9a26b 100644 --- a/api_docs/kbn_core_apps_browser_internal.mdx +++ b/api_docs/kbn_core_apps_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-internal title: "@kbn/core-apps-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 8f8cf31244646..1a913f058009a 100644 --- a/api_docs/kbn_core_apps_browser_mocks.mdx +++ b/api_docs/kbn_core_apps_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-mocks title: "@kbn/core-apps-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 810ea8bb0ca5a..b0c089c8d244e 100644 --- a/api_docs/kbn_core_apps_server_internal.mdx +++ b/api_docs/kbn_core_apps_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-server-internal title: "@kbn/core-apps-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 6094473c1ea27..3f5ffe55370d5 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 6d79790b6aa8e..7ab9d8acacec7 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-common plugin -date: 2023-04-05 +date: 2023-04-06 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 bd0cad1174750..b750091c640a7 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 448449dfe9a83..bf939ec5d280e 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 ae8c42987b82e..1fe2c484bc840 100644 --- a/api_docs/kbn_core_capabilities_browser_mocks.mdx +++ b/api_docs/kbn_core_capabilities_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-browser-mocks title: "@kbn/core-capabilities-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 77ca59d1741e5..2dde36e0dab70 100644 --- a/api_docs/kbn_core_capabilities_common.mdx +++ b/api_docs/kbn_core_capabilities_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-common title: "@kbn/core-capabilities-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-common plugin -date: 2023-04-05 +date: 2023-04-06 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 f364c46c1a44d..49eed9f57ac1c 100644 --- a/api_docs/kbn_core_capabilities_server.mdx +++ b/api_docs/kbn_core_capabilities_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server title: "@kbn/core-capabilities-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server plugin -date: 2023-04-05 +date: 2023-04-06 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 2a05fa233245e..26420321a674a 100644 --- a/api_docs/kbn_core_capabilities_server_mocks.mdx +++ b/api_docs/kbn_core_capabilities_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server-mocks title: "@kbn/core-capabilities-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server-mocks'] --- import kbnCoreCapabilitiesServerMocksObj from './kbn_core_capabilities_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser.mdx b/api_docs/kbn_core_chrome_browser.mdx index 804df1c293068..5f0a942117c55 100644 --- a/api_docs/kbn_core_chrome_browser.mdx +++ b/api_docs/kbn_core_chrome_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser title: "@kbn/core-chrome-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser'] --- import kbnCoreChromeBrowserObj from './kbn_core_chrome_browser.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser_mocks.mdx b/api_docs/kbn_core_chrome_browser_mocks.mdx index 7d56c6ffd1eb4..8e67a1b47ba12 100644 --- a/api_docs/kbn_core_chrome_browser_mocks.mdx +++ b/api_docs/kbn_core_chrome_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser-mocks title: "@kbn/core-chrome-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 881fab764c10a..d56daacc88ecf 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-config-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 33fe62ae793b9..1171bd8f64d16 100644 --- a/api_docs/kbn_core_custom_branding_browser.mdx +++ b/api_docs/kbn_core_custom_branding_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser title: "@kbn/core-custom-branding-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser plugin -date: 2023-04-05 +date: 2023-04-06 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 2df5137ab2050..038354507be69 100644 --- a/api_docs/kbn_core_custom_branding_browser_internal.mdx +++ b/api_docs/kbn_core_custom_branding_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-internal title: "@kbn/core-custom-branding-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 62ad809c45e99..811c217b11e04 100644 --- a/api_docs/kbn_core_custom_branding_browser_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-mocks title: "@kbn/core-custom-branding-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 0d409e598bfb5..de0f314927baa 100644 --- a/api_docs/kbn_core_custom_branding_common.mdx +++ b/api_docs/kbn_core_custom_branding_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-common title: "@kbn/core-custom-branding-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-common plugin -date: 2023-04-05 +date: 2023-04-06 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 efb063231952b..9a4ed4d5ee32d 100644 --- a/api_docs/kbn_core_custom_branding_server.mdx +++ b/api_docs/kbn_core_custom_branding_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server title: "@kbn/core-custom-branding-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server plugin -date: 2023-04-05 +date: 2023-04-06 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 a96aaf27ef47e..ecb3718c37ecb 100644 --- a/api_docs/kbn_core_custom_branding_server_internal.mdx +++ b/api_docs/kbn_core_custom_branding_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-internal title: "@kbn/core-custom-branding-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 ab0fa34c373dc..362a42e139c0e 100644 --- a/api_docs/kbn_core_custom_branding_server_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-mocks title: "@kbn/core-custom-branding-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 ec3df89eacd2b..f95c8ecb349c9 100644 --- a/api_docs/kbn_core_deprecations_browser.mdx +++ b/api_docs/kbn_core_deprecations_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser title: "@kbn/core-deprecations-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser plugin -date: 2023-04-05 +date: 2023-04-06 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 68b6b73f3c197..7048a25ca58ae 100644 --- a/api_docs/kbn_core_deprecations_browser_internal.mdx +++ b/api_docs/kbn_core_deprecations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-internal title: "@kbn/core-deprecations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 ffdb9ee1a30d6..5fd0696bd75ee 100644 --- a/api_docs/kbn_core_deprecations_browser_mocks.mdx +++ b/api_docs/kbn_core_deprecations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-mocks title: "@kbn/core-deprecations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 e82d046b4d2c7..d6b5816eff1c0 100644 --- a/api_docs/kbn_core_deprecations_common.mdx +++ b/api_docs/kbn_core_deprecations_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-common title: "@kbn/core-deprecations-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-common plugin -date: 2023-04-05 +date: 2023-04-06 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 3a973e0f15ca0..3434fd8635de2 100644 --- a/api_docs/kbn_core_deprecations_server.mdx +++ b/api_docs/kbn_core_deprecations_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server title: "@kbn/core-deprecations-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server plugin -date: 2023-04-05 +date: 2023-04-06 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 d9a9eeff1e965..eb7156112ff93 100644 --- a/api_docs/kbn_core_deprecations_server_internal.mdx +++ b/api_docs/kbn_core_deprecations_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-internal title: "@kbn/core-deprecations-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 47cbe8ed30617..56df6b5cd9051 100644 --- a/api_docs/kbn_core_deprecations_server_mocks.mdx +++ b/api_docs/kbn_core_deprecations_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-mocks title: "@kbn/core-deprecations-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 d40b4b1ad4d26..9f96eaa020286 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser plugin -date: 2023-04-05 +date: 2023-04-06 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 8c610ff3fb21f..67085c9220e3b 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks title: "@kbn/core-doc-links-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 2d00905c0ba32..ea8e89ce1d249 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server plugin -date: 2023-04-05 +date: 2023-04-06 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 90ec2181d0c0c..73448d37e1152 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks title: "@kbn/core-doc-links-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 6448d5df865b4..b221eb64a7c25 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-internal title: "@kbn/core-elasticsearch-client-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 d82d1592c910a..ddad347f07155 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-mocks title: "@kbn/core-elasticsearch-client-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 e9d4b8930add0..0cf0e9f0428d2 100644 --- a/api_docs/kbn_core_elasticsearch_server.mdx +++ b/api_docs/kbn_core_elasticsearch_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server title: "@kbn/core-elasticsearch-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server plugin -date: 2023-04-05 +date: 2023-04-06 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 4883134dd263d..cdef7fd2cec20 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-internal title: "@kbn/core-elasticsearch-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 f10b06af23213..414c273821efb 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-mocks title: "@kbn/core-elasticsearch-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 b303b9924e454..03fbff31c8358 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 16f3b89a43a50..0b747adbca7bb 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 cb9b094d138f7..79f5b925bddde 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser plugin -date: 2023-04-05 +date: 2023-04-06 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 ae80c0ce0ef9e..0110de292ba7e 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-internal title: "@kbn/core-execution-context-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 2fca40bc1d564..d312673508853 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-mocks title: "@kbn/core-execution-context-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 c9ae5794b2834..2e121d4c3c0bf 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-common plugin -date: 2023-04-05 +date: 2023-04-06 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 9118c10ce6b24..1881cb4e89318 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server plugin -date: 2023-04-05 +date: 2023-04-06 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 9366ceefc0192..987cfdaa39cf7 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-internal title: "@kbn/core-execution-context-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 0199d86060b2d..95715d1318e67 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-mocks title: "@kbn/core-execution-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 a68a44f716b26..420a82bd92b68 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2023-04-05 +date: 2023-04-06 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 aa79ee10dbce6..8089adabc8973 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser-mocks title: "@kbn/core-fatal-errors-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 c1a0ae9df7fd0..d2913eda00c86 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser plugin -date: 2023-04-05 +date: 2023-04-06 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 4ab68ffb825cb..0213e8a8a8418 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 106dc3845f31a..8df5edc068934 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 476b19f6d5dc7..e337e232efad7 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-common plugin -date: 2023-04-05 +date: 2023-04-06 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 69f6335b53371..4d2da4a5ce698 100644 --- a/api_docs/kbn_core_http_context_server_mocks.mdx +++ b/api_docs/kbn_core_http_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-context-server-mocks title: "@kbn/core-http-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-context-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 3e4be2fc503b0..1fba591729ff4 100644 --- a/api_docs/kbn_core_http_request_handler_context_server.mdx +++ b/api_docs/kbn_core_http_request_handler_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-request-handler-context-server title: "@kbn/core-http-request-handler-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-request-handler-context-server plugin -date: 2023-04-05 +date: 2023-04-06 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 e9d9626755b78..0541072dd29a0 100644 --- a/api_docs/kbn_core_http_resources_server.mdx +++ b/api_docs/kbn_core_http_resources_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server title: "@kbn/core-http-resources-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server plugin -date: 2023-04-05 +date: 2023-04-06 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 4bfcd5cd4ca2e..54e8fb483fb72 100644 --- a/api_docs/kbn_core_http_resources_server_internal.mdx +++ b/api_docs/kbn_core_http_resources_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-internal title: "@kbn/core-http-resources-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 41e3401d70917..1d45cd6e47df2 100644 --- a/api_docs/kbn_core_http_resources_server_mocks.mdx +++ b/api_docs/kbn_core_http_resources_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-mocks title: "@kbn/core-http-resources-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 4b8f31c4d2d25..bca9d16f103f2 100644 --- a/api_docs/kbn_core_http_router_server_internal.mdx +++ b/api_docs/kbn_core_http_router_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-internal title: "@kbn/core-http-router-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 fb9a8bc7cd8f0..c1210dec40196 100644 --- a/api_docs/kbn_core_http_router_server_mocks.mdx +++ b/api_docs/kbn_core_http_router_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-mocks title: "@kbn/core-http-router-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 3d267926e9c25..53fc1ab95c344 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server plugin -date: 2023-04-05 +date: 2023-04-06 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 e32ae1a02575e..21f3d4e758f1e 100644 --- a/api_docs/kbn_core_http_server_internal.mdx +++ b/api_docs/kbn_core_http_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-internal title: "@kbn/core-http-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 fcdfbe56348e0..1475639de1f09 100644 --- a/api_docs/kbn_core_http_server_mocks.mdx +++ b/api_docs/kbn_core_http_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-mocks title: "@kbn/core-http-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 8b83c50dc9d64..76c30a576f015 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser plugin -date: 2023-04-05 +date: 2023-04-06 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 aab07d8bf6852..39b4b2372a7ba 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 4945b1b51494d..855c833d970e8 100644 --- a/api_docs/kbn_core_i18n_server.mdx +++ b/api_docs/kbn_core_i18n_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server title: "@kbn/core-i18n-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server plugin -date: 2023-04-05 +date: 2023-04-06 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 48c8dc88c2c1e..305aa43289d80 100644 --- a/api_docs/kbn_core_i18n_server_internal.mdx +++ b/api_docs/kbn_core_i18n_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-internal title: "@kbn/core-i18n-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 b45f76560f9ce..4fdc9652dc51f 100644 --- a/api_docs/kbn_core_i18n_server_mocks.mdx +++ b/api_docs/kbn_core_i18n_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-mocks title: "@kbn/core-i18n-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 0c172f497462b..058925a62ccf2 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks title: "@kbn/core-injected-metadata-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 cc8066d027095..811c5bd3f2a02 100644 --- a/api_docs/kbn_core_integrations_browser_internal.mdx +++ b/api_docs/kbn_core_integrations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-internal title: "@kbn/core-integrations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 571e8006a4c2e..b814e24c20a6a 100644 --- a/api_docs/kbn_core_integrations_browser_mocks.mdx +++ b/api_docs/kbn_core_integrations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-mocks title: "@kbn/core-integrations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-mocks'] --- import kbnCoreIntegrationsBrowserMocksObj from './kbn_core_integrations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser.mdx b/api_docs/kbn_core_lifecycle_browser.mdx index 2124fac3622df..fc266602e941d 100644 --- a/api_docs/kbn_core_lifecycle_browser.mdx +++ b/api_docs/kbn_core_lifecycle_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser title: "@kbn/core-lifecycle-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser plugin -date: 2023-04-05 +date: 2023-04-06 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 f10a2f9156f13..f8a25c89c45a1 100644 --- a/api_docs/kbn_core_lifecycle_browser_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser-mocks title: "@kbn/core-lifecycle-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 635c44fd65ab9..dfe6708bd9b64 100644 --- a/api_docs/kbn_core_lifecycle_server.mdx +++ b/api_docs/kbn_core_lifecycle_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server title: "@kbn/core-lifecycle-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server plugin -date: 2023-04-05 +date: 2023-04-06 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 8de68a4dd6856..2c67d891b9e63 100644 --- a/api_docs/kbn_core_lifecycle_server_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server-mocks title: "@kbn/core-lifecycle-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 8166852d5cb75..e0dd9fa1338e0 100644 --- a/api_docs/kbn_core_logging_browser_mocks.mdx +++ b/api_docs/kbn_core_logging_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-browser-mocks title: "@kbn/core-logging-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 90897c74c1d22..38fe1076eacee 100644 --- a/api_docs/kbn_core_logging_common_internal.mdx +++ b/api_docs/kbn_core_logging_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-common-internal title: "@kbn/core-logging-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-common-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 159b1bff8b21f..e76ec90f84bff 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server plugin -date: 2023-04-05 +date: 2023-04-06 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 86b1c4f92a365..8bb1196c9c438 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 52ec5b1335df8..0c0a7b1c4509e 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 11c5857da5dbc..27b4ae2381dce 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-internal title: "@kbn/core-metrics-collectors-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 484cf8826f2fc..ee8e1d297f453 100644 --- a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-mocks title: "@kbn/core-metrics-collectors-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 ebc55432ce2c8..95d4ef1084e06 100644 --- a/api_docs/kbn_core_metrics_server.mdx +++ b/api_docs/kbn_core_metrics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server title: "@kbn/core-metrics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server plugin -date: 2023-04-05 +date: 2023-04-06 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 c9705c4e79b62..0e8543cb4beb9 100644 --- a/api_docs/kbn_core_metrics_server_internal.mdx +++ b/api_docs/kbn_core_metrics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-internal title: "@kbn/core-metrics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 26e461ca4de98..0becbf8257d6e 100644 --- a/api_docs/kbn_core_metrics_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-mocks title: "@kbn/core-metrics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 826965acfc6d3..c0e78d8625ea8 100644 --- a/api_docs/kbn_core_mount_utils_browser.mdx +++ b/api_docs/kbn_core_mount_utils_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser title: "@kbn/core-mount-utils-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-mount-utils-browser plugin -date: 2023-04-05 +date: 2023-04-06 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 eb4efac0e46b5..396e1b1e5af63 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server plugin -date: 2023-04-05 +date: 2023-04-06 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 3607b2d6cfb03..afa325ac96bd0 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 e2a02c77259ef..ac2c8330eb434 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 f03a4b2180b0a..7947bc8766570 100644 --- a/api_docs/kbn_core_notifications_browser.mdx +++ b/api_docs/kbn_core_notifications_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser title: "@kbn/core-notifications-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser plugin -date: 2023-04-05 +date: 2023-04-06 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 20346fdca73af..656b7074c2e25 100644 --- a/api_docs/kbn_core_notifications_browser_internal.mdx +++ b/api_docs/kbn_core_notifications_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-internal title: "@kbn/core-notifications-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 9fb0049dd7da7..9b1108437a534 100644 --- a/api_docs/kbn_core_notifications_browser_mocks.mdx +++ b/api_docs/kbn_core_notifications_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-mocks title: "@kbn/core-notifications-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 78b04ee19aea1..c8dcd33e7f07d 100644 --- a/api_docs/kbn_core_overlays_browser.mdx +++ b/api_docs/kbn_core_overlays_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser title: "@kbn/core-overlays-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser plugin -date: 2023-04-05 +date: 2023-04-06 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 b192911f284d3..ed0415d921877 100644 --- a/api_docs/kbn_core_overlays_browser_internal.mdx +++ b/api_docs/kbn_core_overlays_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-internal title: "@kbn/core-overlays-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 81795d650f18d..e1c5ea01b4cb5 100644 --- a/api_docs/kbn_core_overlays_browser_mocks.mdx +++ b/api_docs/kbn_core_overlays_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-mocks title: "@kbn/core-overlays-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 f041e6843e433..e5ffaac8bb71d 100644 --- a/api_docs/kbn_core_plugins_browser.mdx +++ b/api_docs/kbn_core_plugins_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser title: "@kbn/core-plugins-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser plugin -date: 2023-04-05 +date: 2023-04-06 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 cf539d36ff189..97d7c292110cf 100644 --- a/api_docs/kbn_core_plugins_browser_mocks.mdx +++ b/api_docs/kbn_core_plugins_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser-mocks title: "@kbn/core-plugins-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 5491f248f8713..e739ef8c7018d 100644 --- a/api_docs/kbn_core_plugins_server.mdx +++ b/api_docs/kbn_core_plugins_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server title: "@kbn/core-plugins-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server plugin -date: 2023-04-05 +date: 2023-04-06 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 bb5533762f706..0fa82b0742113 100644 --- a/api_docs/kbn_core_plugins_server_mocks.mdx +++ b/api_docs/kbn_core_plugins_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server-mocks title: "@kbn/core-plugins-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 edfd2a2ee95f8..d5234c7423bf0 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server plugin -date: 2023-04-05 +date: 2023-04-06 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 9adced981a3b5..f863d68304b7c 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 688a8ce9ef8fb..669869fd2a5ea 100644 --- a/api_docs/kbn_core_rendering_browser_mocks.mdx +++ b/api_docs/kbn_core_rendering_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-browser-mocks title: "@kbn/core-rendering-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 248fcb1bf2193..305e7573a9d6a 100644 --- a/api_docs/kbn_core_rendering_server_internal.mdx +++ b/api_docs/kbn_core_rendering_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-internal title: "@kbn/core-rendering-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 b14a000990a29..c6de24eaf9e11 100644 --- a/api_docs/kbn_core_rendering_server_mocks.mdx +++ b/api_docs/kbn_core_rendering_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-mocks title: "@kbn/core-rendering-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 eed16ffb05ee5..1538f08e4b5cd 100644 --- a/api_docs/kbn_core_root_server_internal.mdx +++ b/api_docs/kbn_core_root_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-root-server-internal title: "@kbn/core-root-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-root-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 178c4744f635b..5c9e5baf7b933 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.mdx +++ b/api_docs/kbn_core_saved_objects_api_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-browser title: "@kbn/core-saved-objects-api-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-browser plugin -date: 2023-04-05 +date: 2023-04-06 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 b6a4d3005855d..a7aae41747f54 100644 --- a/api_docs/kbn_core_saved_objects_api_server.mdx +++ b/api_docs/kbn_core_saved_objects_api_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server title: "@kbn/core-saved-objects-api-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server plugin -date: 2023-04-05 +date: 2023-04-06 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 3146f232d5b90..297eab286caca 100644 --- a/api_docs/kbn_core_saved_objects_api_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-internal title: "@kbn/core-saved-objects-api-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 2431a86a119ce..8d2cc14bc97ba 100644 --- a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-mocks title: "@kbn/core-saved-objects-api-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-mocks'] --- import kbnCoreSavedObjectsApiServerMocksObj from './kbn_core_saved_objects_api_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_internal.mdx b/api_docs/kbn_core_saved_objects_base_server_internal.mdx index 2eee041095c42..0b69340170f15 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-internal title: "@kbn/core-saved-objects-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-internal'] --- import kbnCoreSavedObjectsBaseServerInternalObj from './kbn_core_saved_objects_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx index 4c970d58e8ee9..bd73dfe6da89a 100644 --- a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-mocks title: "@kbn/core-saved-objects-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 e5df32f55eb25..f2780f6851d1a 100644 --- a/api_docs/kbn_core_saved_objects_browser.mdx +++ b/api_docs/kbn_core_saved_objects_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser title: "@kbn/core-saved-objects-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser plugin -date: 2023-04-05 +date: 2023-04-06 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 d098e4beab6d6..2becd4a721eb6 100644 --- a/api_docs/kbn_core_saved_objects_browser_internal.mdx +++ b/api_docs/kbn_core_saved_objects_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-internal title: "@kbn/core-saved-objects-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 5eea708936e0f..3ab8b619efdeb 100644 --- a/api_docs/kbn_core_saved_objects_browser_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-mocks title: "@kbn/core-saved-objects-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 388d6e7a1e11d..cf8fa13f38d8f 100644 --- a/api_docs/kbn_core_saved_objects_common.mdx +++ b/api_docs/kbn_core_saved_objects_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-common title: "@kbn/core-saved-objects-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-common plugin -date: 2023-04-05 +date: 2023-04-06 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 b12c948761e63..87564ac336f36 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-internal title: "@kbn/core-saved-objects-import-export-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 cfeb245e6330e..1b16c84e1c29c 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-mocks title: "@kbn/core-saved-objects-import-export-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 54729f3f5e6e9..85fb027ddaf97 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-internal title: "@kbn/core-saved-objects-migration-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 fde4667e9fe8b..944b8b2b0b7d6 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-mocks title: "@kbn/core-saved-objects-migration-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 3bcada8278c18..c3268374f5aac 100644 --- a/api_docs/kbn_core_saved_objects_server.mdx +++ b/api_docs/kbn_core_saved_objects_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server title: "@kbn/core-saved-objects-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server plugin -date: 2023-04-05 +date: 2023-04-06 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 6c162664c1bca..b2b6737749a84 100644 --- a/api_docs/kbn_core_saved_objects_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-internal title: "@kbn/core-saved-objects-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 5941356bfe4f7..a1ae5b73b0a9a 100644 --- a/api_docs/kbn_core_saved_objects_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-mocks title: "@kbn/core-saved-objects-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 3de87388b1f47..ec589425cdb88 100644 --- a/api_docs/kbn_core_saved_objects_utils_server.mdx +++ b/api_docs/kbn_core_saved_objects_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-utils-server title: "@kbn/core-saved-objects-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-utils-server plugin -date: 2023-04-05 +date: 2023-04-06 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 19fd803b0df4c..c54b852680bee 100644 --- a/api_docs/kbn_core_status_common.mdx +++ b/api_docs/kbn_core_status_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common title: "@kbn/core-status-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common plugin -date: 2023-04-05 +date: 2023-04-06 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 164eb3fbfc302..1103b78cd19bd 100644 --- a/api_docs/kbn_core_status_common_internal.mdx +++ b/api_docs/kbn_core_status_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common-internal title: "@kbn/core-status-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 775ff4b2e0cbb..edf3544ecaed7 100644 --- a/api_docs/kbn_core_status_server.mdx +++ b/api_docs/kbn_core_status_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server title: "@kbn/core-status-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server plugin -date: 2023-04-05 +date: 2023-04-06 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 ea550035a7dbe..1db2ad3d29248 100644 --- a/api_docs/kbn_core_status_server_internal.mdx +++ b/api_docs/kbn_core_status_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-internal title: "@kbn/core-status-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 5bf498e570d5a..3844ca7ed81e0 100644 --- a/api_docs/kbn_core_status_server_mocks.mdx +++ b/api_docs/kbn_core_status_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-mocks title: "@kbn/core-status-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 0e3d59316dcec..5ea2573cfcba6 100644 --- a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-deprecations-getters title: "@kbn/core-test-helpers-deprecations-getters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-deprecations-getters plugin -date: 2023-04-05 +date: 2023-04-06 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 e9de3dbd72c73..762c0d15f941e 100644 --- a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx +++ b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-http-setup-browser title: "@kbn/core-test-helpers-http-setup-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-http-setup-browser plugin -date: 2023-04-05 +date: 2023-04-06 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 a90db88cbf72d..252a9d37902b3 100644 --- a/api_docs/kbn_core_test_helpers_kbn_server.mdx +++ b/api_docs/kbn_core_test_helpers_kbn_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-kbn-server title: "@kbn/core-test-helpers-kbn-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-kbn-server plugin -date: 2023-04-05 +date: 2023-04-06 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 df33e13e55b93..f47dd01757ad2 100644 --- a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx +++ b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-so-type-serializer title: "@kbn/core-test-helpers-so-type-serializer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-so-type-serializer plugin -date: 2023-04-05 +date: 2023-04-06 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 0b5573a07bb9a..673554ef28ac6 100644 --- a/api_docs/kbn_core_test_helpers_test_utils.mdx +++ b/api_docs/kbn_core_test_helpers_test_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-test-utils title: "@kbn/core-test-helpers-test-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-test-utils plugin -date: 2023-04-05 +date: 2023-04-06 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 422abfd61a19d..de491de113f1f 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser plugin -date: 2023-04-05 +date: 2023-04-06 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 992447c0d82e5..c0aee68600e19 100644 --- a/api_docs/kbn_core_theme_browser_internal.mdx +++ b/api_docs/kbn_core_theme_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-internal title: "@kbn/core-theme-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 2b408360b12f8..3add67ec416c4 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 2280a671bb9fd..0770a4c28bc85 100644 --- a/api_docs/kbn_core_ui_settings_browser.mdx +++ b/api_docs/kbn_core_ui_settings_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser title: "@kbn/core-ui-settings-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser plugin -date: 2023-04-05 +date: 2023-04-06 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 fbdaab0600122..1165cd1cf425e 100644 --- a/api_docs/kbn_core_ui_settings_browser_internal.mdx +++ b/api_docs/kbn_core_ui_settings_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-internal title: "@kbn/core-ui-settings-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 97655a4481f31..6aa73ecd31558 100644 --- a/api_docs/kbn_core_ui_settings_browser_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-mocks title: "@kbn/core-ui-settings-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 5c49b7b8357e9..416504f5381e7 100644 --- a/api_docs/kbn_core_ui_settings_common.mdx +++ b/api_docs/kbn_core_ui_settings_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-common title: "@kbn/core-ui-settings-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-common plugin -date: 2023-04-05 +date: 2023-04-06 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 f3c29f2a5e53d..6b8869d7edab5 100644 --- a/api_docs/kbn_core_ui_settings_server.mdx +++ b/api_docs/kbn_core_ui_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server title: "@kbn/core-ui-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server plugin -date: 2023-04-05 +date: 2023-04-06 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 5589df3657283..776d875d8adfe 100644 --- a/api_docs/kbn_core_ui_settings_server_internal.mdx +++ b/api_docs/kbn_core_ui_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-internal title: "@kbn/core-ui-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 8671354a45768..699518adbcdcf 100644 --- a/api_docs/kbn_core_ui_settings_server_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-mocks title: "@kbn/core-ui-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 0af739064fd51..d3c70c0aed629 100644 --- a/api_docs/kbn_core_usage_data_server.mdx +++ b/api_docs/kbn_core_usage_data_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server title: "@kbn/core-usage-data-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server plugin -date: 2023-04-05 +date: 2023-04-06 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 6d05190590cf0..9b5a031692970 100644 --- a/api_docs/kbn_core_usage_data_server_internal.mdx +++ b/api_docs/kbn_core_usage_data_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-internal title: "@kbn/core-usage-data-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-internal plugin -date: 2023-04-05 +date: 2023-04-06 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 6650099f7eb68..7e3a6d40ce2ab 100644 --- a/api_docs/kbn_core_usage_data_server_mocks.mdx +++ b/api_docs/kbn_core_usage_data_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-mocks title: "@kbn/core-usage-data-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-mocks plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-mocks'] --- import kbnCoreUsageDataServerMocksObj from './kbn_core_usage_data_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx index ccb531f7f5a9f..14e42a78f6cb9 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto plugin -date: 2023-04-05 +date: 2023-04-06 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 d38c34390591d..8909fd72980b5 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto-browser plugin -date: 2023-04-05 +date: 2023-04-06 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 72528ac245aab..111dae5978ce4 100644 --- a/api_docs/kbn_cypress_config.mdx +++ b/api_docs/kbn_cypress_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cypress-config title: "@kbn/cypress-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cypress-config plugin -date: 2023-04-05 +date: 2023-04-06 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 35d84cd141c78..53958fb676de2 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/datemath plugin -date: 2023-04-05 +date: 2023-04-06 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 42e36c10fb4c0..729cc299d791b 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-errors plugin -date: 2023-04-05 +date: 2023-04-06 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 a921d5b160e7c..941d68af851a7 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-runner plugin -date: 2023-04-05 +date: 2023-04-06 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 29d544be2a1c9..fa357c3284d4e 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-proc-runner plugin -date: 2023-04-05 +date: 2023-04-06 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 3c17a5d2f03d5..a26c034ec5d0f 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-utils plugin -date: 2023-04-05 +date: 2023-04-06 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 c3d5aab1a96e4..125cf72a3f237 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/doc-links plugin -date: 2023-04-05 +date: 2023-04-06 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 ab45e37a904f5..618e5d01a1c61 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/docs-utils plugin -date: 2023-04-05 +date: 2023-04-06 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 7c3b81cc8e5c5..59592ac58a53d 100644 --- a/api_docs/kbn_dom_drag_drop.mdx +++ b/api_docs/kbn_dom_drag_drop.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dom-drag-drop title: "@kbn/dom-drag-drop" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dom-drag-drop plugin -date: 2023-04-05 +date: 2023-04-06 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 8f317572ffb5b..77148513b75dd 100644 --- a/api_docs/kbn_ebt_tools.mdx +++ b/api_docs/kbn_ebt_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ebt-tools title: "@kbn/ebt-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ebt-tools plugin -date: 2023-04-05 +date: 2023-04-06 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 c486777fdd9d9..6404cf7092512 100644 --- a/api_docs/kbn_ecs.mdx +++ b/api_docs/kbn_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs title: "@kbn/ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs plugin -date: 2023-04-05 +date: 2023-04-06 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 2e184fd3652fc..dd4e876477cfa 100644 --- a/api_docs/kbn_ecs_data_quality_dashboard.mdx +++ b/api_docs/kbn_ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs-data-quality-dashboard title: "@kbn/ecs-data-quality-dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs-data-quality-dashboard plugin -date: 2023-04-05 +date: 2023-04-06 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 a61164e5b88d3..36c0aa987c7e6 100644 --- a/api_docs/kbn_es.mdx +++ b/api_docs/kbn_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es title: "@kbn/es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es plugin -date: 2023-04-05 +date: 2023-04-06 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 d2207665a3429..a772965f4212b 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-archiver plugin -date: 2023-04-05 +date: 2023-04-06 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 fb478bf946753..05d4f7321d1eb 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-errors plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.devdocs.json b/api_docs/kbn_es_query.devdocs.json index 15f5ca2dcacdb..84a193f3eb7b8 100644 --- a/api_docs/kbn_es_query.devdocs.json +++ b/api_docs/kbn_es_query.devdocs.json @@ -4241,6 +4241,8 @@ "FilterMetaParams", " | undefined; length: number; toString(): string; toLocaleString(): string; pop(): boolean | undefined; push(...items: boolean[]): number; concat(...items: ConcatArray[]): boolean[]; concat(...items: (boolean | ConcatArray)[]): boolean[]; join(separator?: string | undefined): string; reverse(): boolean[]; shift(): boolean | undefined; slice(start?: number | undefined, end?: number | undefined): boolean[]; sort(compareFn?: ((a: boolean, b: boolean) => number) | undefined): boolean[]; splice(start: number, deleteCount?: number | undefined): boolean[]; splice(start: number, deleteCount: number, ...items: boolean[]): boolean[]; unshift(...items: boolean[]): number; indexOf(searchElement: boolean, fromIndex?: number | undefined): number; lastIndexOf(searchElement: boolean, fromIndex?: number | undefined): number; every(predicate: (value: boolean, index: number, array: boolean[]) => value is S, thisArg?: any): this is S[]; every(predicate: (value: boolean, index: number, array: boolean[]) => unknown, thisArg?: any): boolean; some(predicate: (value: boolean, index: number, array: boolean[]) => unknown, thisArg?: any): boolean; forEach(callbackfn: (value: boolean, index: number, array: boolean[]) => void, thisArg?: any): void; map(callbackfn: (value: boolean, index: number, array: boolean[]) => U, thisArg?: any): U[]; filter(predicate: (value: boolean, index: number, array: boolean[]) => value is S, thisArg?: any): S[]; filter(predicate: (value: boolean, index: number, array: boolean[]) => unknown, thisArg?: any): boolean[]; reduce(callbackfn: (previousValue: boolean, currentValue: boolean, currentIndex: number, array: boolean[]) => boolean): boolean; reduce(callbackfn: (previousValue: boolean, currentValue: boolean, currentIndex: number, array: boolean[]) => boolean, initialValue: boolean): boolean; reduce(callbackfn: (previousValue: U, currentValue: boolean, currentIndex: number, array: boolean[]) => U, initialValue: U): U; reduceRight(callbackfn: (previousValue: boolean, currentValue: boolean, currentIndex: number, array: boolean[]) => boolean): boolean; reduceRight(callbackfn: (previousValue: boolean, currentValue: boolean, currentIndex: number, array: boolean[]) => boolean, initialValue: boolean): boolean; reduceRight(callbackfn: (previousValue: U, currentValue: boolean, currentIndex: number, array: boolean[]) => U, initialValue: U): U; find(predicate: (this: void, value: boolean, index: number, obj: boolean[]) => value is S, thisArg?: any): S | undefined; find(predicate: (value: boolean, index: number, obj: boolean[]) => unknown, thisArg?: any): boolean | undefined; findIndex(predicate: (value: boolean, index: number, obj: boolean[]) => unknown, thisArg?: any): number; fill(value: boolean, start?: number | undefined, end?: number | undefined): boolean[]; copyWithin(target: number, start: number, end?: number | undefined): boolean[]; entries(): IterableIterator<[number, boolean]>; keys(): IterableIterator; values(): IterableIterator; includes(searchElement: boolean, fromIndex?: number | undefined): boolean; flatMap(callback: (this: This, value: boolean, index: number, array: boolean[]) => U | readonly U[], thisArg?: This | undefined): U[]; flat(this: A, depth?: D | undefined): FlatArray[]; [Symbol.iterator](): IterableIterator; [Symbol.unscopables](): { copyWithin: boolean; entries: boolean; fill: boolean; find: boolean; findIndex: boolean; keys: boolean; values: boolean; }; at(index: number): boolean | undefined; } | { query: ", "FilterMetaParams", + " | undefined; from?: string | number | undefined; to?: string | number | undefined; gt?: string | number | undefined; lt?: string | number | undefined; gte?: string | number | undefined; lte?: string | number | undefined; format?: string | undefined; } | { query: ", + "FilterMetaParams", " | undefined; length: number; toString(): string; toLocaleString(): string; pop(): number | undefined; push(...items: number[]): number; concat(...items: ConcatArray[]): number[]; concat(...items: (number | ConcatArray)[]): number[]; join(separator?: string | undefined): string; reverse(): number[]; shift(): number | undefined; slice(start?: number | undefined, end?: number | undefined): number[]; sort(compareFn?: ((a: number, b: number) => number) | undefined): number[]; splice(start: number, deleteCount?: number | undefined): number[]; splice(start: number, deleteCount: number, ...items: number[]): number[]; unshift(...items: number[]): number; indexOf(searchElement: number, fromIndex?: number | undefined): number; lastIndexOf(searchElement: number, fromIndex?: number | undefined): number; every(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; every(predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; some(predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; forEach(callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any): void; map(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any): U[]; filter(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S[]; filter(predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; reduce(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; reduceRight(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; find(predicate: (this: void, value: number, index: number, obj: number[]) => value is S, thisArg?: any): S | undefined; find(predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number | undefined; findIndex(predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number; fill(value: number, start?: number | undefined, end?: number | undefined): number[]; copyWithin(target: number, start: number, end?: number | undefined): number[]; entries(): IterableIterator<[number, number]>; keys(): IterableIterator; values(): IterableIterator; includes(searchElement: number, fromIndex?: number | undefined): boolean; flatMap(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This | undefined): U[]; flat(this: A, depth?: D | undefined): FlatArray[]; [Symbol.iterator](): IterableIterator; [Symbol.unscopables](): { copyWithin: boolean; entries: boolean; fill: boolean; find: boolean; findIndex: boolean; keys: boolean; values: boolean; }; at(index: number): number | undefined; } | { query: ", "FilterMetaParams", " | undefined; $state?: { store: ", @@ -4915,8 +4917,6 @@ }, ") | undefined; value?: string | undefined; field?: string | undefined; formattedValue?: string | undefined; } | { query: ", "FilterMetaParams", - " | undefined; from?: string | number | undefined; to?: string | number | undefined; gt?: string | number | undefined; lt?: string | number | undefined; gte?: string | number | undefined; lte?: string | number | undefined; format?: string | undefined; } | { query: ", - "FilterMetaParams", " | undefined; alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: (", "FilterMetaParams", " & ", @@ -6580,6 +6580,75 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.nodeBuilder.range", + "type": "Function", + "tags": [], + "label": "range", + "description": [], + "signature": [ + "(fieldName: string, operator: \"gt\" | \"gte\" | \"lt\" | \"lte\", value: string | number) => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.FunctionTypeBuildNode", + "text": "FunctionTypeBuildNode" + } + ], + "path": "packages/kbn-es-query/src/kuery/node_types/node_builder.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.nodeBuilder.range.$1", + "type": "string", + "tags": [], + "label": "fieldName", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-es-query/src/kuery/node_types/node_builder.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.nodeBuilder.range.$2", + "type": "CompoundType", + "tags": [], + "label": "operator", + "description": [], + "signature": [ + "\"gt\" | \"gte\" | \"lt\" | \"lte\"" + ], + "path": "packages/kbn-es-query/src/kuery/node_types/node_builder.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.nodeBuilder.range.$3", + "type": "CompoundType", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "string | number" + ], + "path": "packages/kbn-es-query/src/kuery/node_types/node_builder.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] } ], "initialIsOpen": false diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index 6696127236e10..a22d75a1925a0 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-query plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] --- import kbnEsQueryObj from './kbn_es_query.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 251 | 1 | 193 | 15 | +| 255 | 1 | 197 | 15 | ## Common diff --git a/api_docs/kbn_es_types.mdx b/api_docs/kbn_es_types.mdx index 613dfcc01f7a0..fc1fbde3d6b4a 100644 --- a/api_docs/kbn_es_types.mdx +++ b/api_docs/kbn_es_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-types title: "@kbn/es-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-types plugin -date: 2023-04-05 +date: 2023-04-06 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 07cbcd5c3b572..110281f071c4d 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/eslint-plugin-imports plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_expandable_flyout.devdocs.json b/api_docs/kbn_expandable_flyout.devdocs.json index 9658f381fd343..1590d2e5ac52d 100644 --- a/api_docs/kbn_expandable_flyout.devdocs.json +++ b/api_docs/kbn_expandable_flyout.devdocs.json @@ -26,7 +26,7 @@ "tags": [], "label": "ExpandableFlyout", "description": [ - "\nExpandable flyout UI React component.\nDisplays 3 sections (right, left, preview) depending on the panels in the context." + "\nExpandable flyout UI React component.\nDisplays 3 sections (right, left, preview) depending on the panels in the context.\n\nThe behavior expects that the left and preview sections should only be displayed is a right section\nis already rendered." ], "signature": [ "{ ({ registeredPanels, handleOnFlyoutClosed, ...flyoutProps }: React.PropsWithChildren<", diff --git a/api_docs/kbn_expandable_flyout.mdx b/api_docs/kbn_expandable_flyout.mdx index 20387e663bb1b..dbbfeb4ccf37d 100644 --- a/api_docs/kbn_expandable_flyout.mdx +++ b/api_docs/kbn_expandable_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-expandable-flyout title: "@kbn/expandable-flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/expandable-flyout plugin -date: 2023-04-05 +date: 2023-04-06 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 b3e0da77bf7de..59e68fb89b65e 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-types plugin -date: 2023-04-05 +date: 2023-04-06 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 db0cf908f6e63..d376396c3d233 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/find-used-node-modules plugin -date: 2023-04-05 +date: 2023-04-06 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 d0af5805d99ad..113f9993967ab 100644 --- a/api_docs/kbn_ftr_common_functional_services.mdx +++ b/api_docs/kbn_ftr_common_functional_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-services title: "@kbn/ftr-common-functional-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-services plugin -date: 2023-04-05 +date: 2023-04-06 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 baef5adfacc25..d1b9374c6cff0 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index dcde8c3a63b70..d35e8627cb782 100644 --- a/api_docs/kbn_guided_onboarding.mdx +++ b/api_docs/kbn_guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-guided-onboarding title: "@kbn/guided-onboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/guided-onboarding plugin -date: 2023-04-05 +date: 2023-04-06 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 21259fe1e3f96..135d591a5a6d1 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/handlebars plugin -date: 2023-04-05 +date: 2023-04-06 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 ee8514cd65063..a6b7e142115ce 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/hapi-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 88f4a5c175abc..0107ce07860dc 100644 --- a/api_docs/kbn_health_gateway_server.mdx +++ b/api_docs/kbn_health_gateway_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-health-gateway-server title: "@kbn/health-gateway-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/health-gateway-server plugin -date: 2023-04-05 +date: 2023-04-06 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 2b53c95f3bc3e..fd57ffc4b209a 100644 --- a/api_docs/kbn_home_sample_data_card.mdx +++ b/api_docs/kbn_home_sample_data_card.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-card title: "@kbn/home-sample-data-card" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-card plugin -date: 2023-04-05 +date: 2023-04-06 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 f99265bc075c7..f5973c4057f2e 100644 --- a/api_docs/kbn_home_sample_data_tab.mdx +++ b/api_docs/kbn_home_sample_data_tab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-tab title: "@kbn/home-sample-data-tab" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-tab plugin -date: 2023-04-05 +date: 2023-04-06 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 7843709930148..ec4d4928fe623 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n plugin -date: 2023-04-05 +date: 2023-04-06 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 f4e58f84f9f07..509bd34892774 100644 --- a/api_docs/kbn_i18n_react.mdx +++ b/api_docs/kbn_i18n_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n-react title: "@kbn/i18n-react" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n-react plugin -date: 2023-04-05 +date: 2023-04-06 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 be62f87b848b0..2eeb8438b47a2 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/import-resolver plugin -date: 2023-04-05 +date: 2023-04-06 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 1ac52d3dd3cea..6604ba58b8789 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/interpreter plugin -date: 2023-04-05 +date: 2023-04-06 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 e3c23df20934e..27266775f0882 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/io-ts-utils plugin -date: 2023-04-05 +date: 2023-04-06 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 ff12b958aa0f7..6fc09a5892719 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/jest-serializers plugin -date: 2023-04-05 +date: 2023-04-06 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 e121cb8455f3f..82b18e6d1cd26 100644 --- a/api_docs/kbn_journeys.mdx +++ b/api_docs/kbn_journeys.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-journeys title: "@kbn/journeys" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/journeys plugin -date: 2023-04-05 +date: 2023-04-06 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 64324459d03e1..1be5823f363c7 100644 --- a/api_docs/kbn_json_ast.mdx +++ b/api_docs/kbn_json_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-ast title: "@kbn/json-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-ast plugin -date: 2023-04-05 +date: 2023-04-06 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 67ef8c68137e7..d3f2035cf4762 100644 --- a/api_docs/kbn_kibana_manifest_schema.mdx +++ b/api_docs/kbn_kibana_manifest_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-schema title: "@kbn/kibana-manifest-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-schema plugin -date: 2023-04-05 +date: 2023-04-06 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 bc19e77e3d70b..f56a0ac466456 100644 --- a/api_docs/kbn_language_documentation_popover.mdx +++ b/api_docs/kbn_language_documentation_popover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-language-documentation-popover title: "@kbn/language-documentation-popover" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/language-documentation-popover plugin -date: 2023-04-05 +date: 2023-04-06 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 88b05f79a7795..99d912ee08892 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging plugin -date: 2023-04-05 +date: 2023-04-06 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 6d665aae8d8d5..2e0cf286cae0e 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 7048c8ef9146b..34785af9909a1 100644 --- a/api_docs/kbn_managed_vscode_config.mdx +++ b/api_docs/kbn_managed_vscode_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-vscode-config title: "@kbn/managed-vscode-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-vscode-config plugin -date: 2023-04-05 +date: 2023-04-06 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 fe47eb8c1c932..d314d78903e06 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mapbox-gl plugin -date: 2023-04-05 +date: 2023-04-06 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 8d1b1a805cd4e..efe9a0de1f819 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-agg-utils plugin -date: 2023-04-05 +date: 2023-04-06 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 f6af08105963f..aad7ef90858aa 100644 --- a/api_docs/kbn_ml_date_picker.mdx +++ b/api_docs/kbn_ml_date_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-picker title: "@kbn/ml-date-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-picker plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-picker'] --- import kbnMlDatePickerObj from './kbn_ml_date_picker.devdocs.json'; diff --git a/api_docs/kbn_ml_is_defined.mdx b/api_docs/kbn_ml_is_defined.mdx index 2d93f0a616ed8..76e51061150ca 100644 --- a/api_docs/kbn_ml_is_defined.mdx +++ b/api_docs/kbn_ml_is_defined.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-defined title: "@kbn/ml-is-defined" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-defined plugin -date: 2023-04-05 +date: 2023-04-06 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 3b62bf7980597..eebe33b2f94df 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-populated-object plugin -date: 2023-04-05 +date: 2023-04-06 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 d5a35a419bd77..3e2a158ddbe49 100644 --- a/api_docs/kbn_ml_local_storage.mdx +++ b/api_docs/kbn_ml_local_storage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-local-storage title: "@kbn/ml-local-storage" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-local-storage plugin -date: 2023-04-05 +date: 2023-04-06 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 e28f8d164155b..968c18dcd208b 100644 --- a/api_docs/kbn_ml_nested_property.mdx +++ b/api_docs/kbn_ml_nested_property.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-nested-property title: "@kbn/ml-nested-property" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-nested-property plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-nested-property'] --- import kbnMlNestedPropertyObj from './kbn_ml_nested_property.devdocs.json'; diff --git a/api_docs/kbn_ml_query_utils.mdx b/api_docs/kbn_ml_query_utils.mdx index 9e84abb7af7c7..6b46b84b456ce 100644 --- a/api_docs/kbn_ml_query_utils.mdx +++ b/api_docs/kbn_ml_query_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-query-utils title: "@kbn/ml-query-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-query-utils plugin -date: 2023-04-05 +date: 2023-04-06 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_route_utils.mdx b/api_docs/kbn_ml_route_utils.mdx index 566d0bf240f1f..3d69a899cf7a5 100644 --- a/api_docs/kbn_ml_route_utils.mdx +++ b/api_docs/kbn_ml_route_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-route-utils title: "@kbn/ml-route-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-route-utils plugin -date: 2023-04-05 +date: 2023-04-06 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 7f48df0093172..ce20a94cae4a0 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-string-hash plugin -date: 2023-04-05 +date: 2023-04-06 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 efad4d349ee73..1fa85a1727221 100644 --- a/api_docs/kbn_ml_trained_models_utils.mdx +++ b/api_docs/kbn_ml_trained_models_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-trained-models-utils title: "@kbn/ml-trained-models-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-trained-models-utils plugin -date: 2023-04-05 +date: 2023-04-06 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 25ce3e2e17b04..7720df2a45de3 100644 --- a/api_docs/kbn_ml_url_state.mdx +++ b/api_docs/kbn_ml_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-url-state title: "@kbn/ml-url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-url-state plugin -date: 2023-04-05 +date: 2023-04-06 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 29dc77e1e14a5..bf0ff70120803 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/monaco plugin -date: 2023-04-05 +date: 2023-04-06 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 a67fae7cdcda6..25085ad6c65d0 100644 --- a/api_docs/kbn_object_versioning.mdx +++ b/api_docs/kbn_object_versioning.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-object-versioning title: "@kbn/object-versioning" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/object-versioning plugin -date: 2023-04-05 +date: 2023-04-06 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.devdocs.json b/api_docs/kbn_observability_alert_details.devdocs.json index 4218f92ea3dfd..a7f16d862b6d7 100644 --- a/api_docs/kbn_observability_alert_details.devdocs.json +++ b/api_docs/kbn_observability_alert_details.devdocs.json @@ -19,6 +19,39 @@ "common": { "classes": [], "functions": [ + { + "parentPluginId": "@kbn/observability-alert-details", + "id": "def-common.AlertActiveTimeRangeAnnotation", + "type": "Function", + "tags": [], + "label": "AlertActiveTimeRangeAnnotation", + "description": [], + "signature": [ + "({ alertStart, alertEnd, color, id }: Props) => JSX.Element" + ], + "path": "x-pack/packages/observability/alert_details/src/components/alert_active_time_range_annotation.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/observability-alert-details", + "id": "def-common.AlertActiveTimeRangeAnnotation.$1", + "type": "Object", + "tags": [], + "label": "{ alertStart, alertEnd, color, id }", + "description": [], + "signature": [ + "Props" + ], + "path": "x-pack/packages/observability/alert_details/src/components/alert_active_time_range_annotation.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/observability-alert-details", "id": "def-common.AlertAnnotation", @@ -54,22 +87,22 @@ }, { "parentPluginId": "@kbn/observability-alert-details", - "id": "def-common.getAlertTimeRange", + "id": "def-common.getPaddedAlertTimeRange", "type": "Function", "tags": [], - "label": "getAlertTimeRange", + "label": "getPaddedAlertTimeRange", "description": [], "signature": [ "(alertStart: string, alertEnd?: string | undefined) => ", "TimeRange" ], - "path": "x-pack/packages/observability/alert_details/src/helpers/get_alert_time_range.ts", + "path": "x-pack/packages/observability/alert_details/src/helpers/get_padded_alert_time_range.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/observability-alert-details", - "id": "def-common.getAlertTimeRange.$1", + "id": "def-common.getPaddedAlertTimeRange.$1", "type": "string", "tags": [], "label": "alertStart", @@ -77,14 +110,14 @@ "signature": [ "string" ], - "path": "x-pack/packages/observability/alert_details/src/helpers/get_alert_time_range.ts", + "path": "x-pack/packages/observability/alert_details/src/helpers/get_padded_alert_time_range.ts", "deprecated": false, "trackAdoption": false, "isRequired": true }, { "parentPluginId": "@kbn/observability-alert-details", - "id": "def-common.getAlertTimeRange.$2", + "id": "def-common.getPaddedAlertTimeRange.$2", "type": "string", "tags": [], "label": "alertEnd", @@ -92,7 +125,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/packages/observability/alert_details/src/helpers/get_alert_time_range.ts", + "path": "x-pack/packages/observability/alert_details/src/helpers/get_padded_alert_time_range.ts", "deprecated": false, "trackAdoption": false, "isRequired": false diff --git a/api_docs/kbn_observability_alert_details.mdx b/api_docs/kbn_observability_alert_details.mdx index 7497a4c2547e7..c1732f67d26ba 100644 --- a/api_docs/kbn_observability_alert_details.mdx +++ b/api_docs/kbn_observability_alert_details.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alert-details title: "@kbn/observability-alert-details" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alert-details plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alert-details'] --- import kbnObservabilityAlertDetailsObj from './kbn_observability_alert_details.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/actionable-observability](https://github.com/orgs/elastic/team | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 5 | 0 | 5 | 1 | +| 7 | 0 | 7 | 1 | ## Common diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index c1ad846ce90cb..5069b23f37604 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer plugin -date: 2023-04-05 +date: 2023-04-06 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 324a106c2c5fd..56b7f74be3f43 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2023-04-05 +date: 2023-04-06 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 6828fad68972b..e02975b650ed2 100644 --- a/api_docs/kbn_osquery_io_ts_types.mdx +++ b/api_docs/kbn_osquery_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-osquery-io-ts-types title: "@kbn/osquery-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/osquery-io-ts-types plugin -date: 2023-04-05 +date: 2023-04-06 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 54531f07faf37..e247d12f72e37 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2023-04-05 +date: 2023-04-06 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 5b457d3287370..944f1f2db6692 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-generator plugin -date: 2023-04-05 +date: 2023-04-06 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 2ea48935f4930..9ea01d4768c6a 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-helpers plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index 268ee4214c5ed..e894bcab51255 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-field plugin -date: 2023-04-05 +date: 2023-04-06 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 46ee185f71113..6333ac7d2c242 100644 --- a/api_docs/kbn_repo_file_maps.mdx +++ b/api_docs/kbn_repo_file_maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-file-maps title: "@kbn/repo-file-maps" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-file-maps plugin -date: 2023-04-05 +date: 2023-04-06 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 d558d907181d8..c05b34e345065 100644 --- a/api_docs/kbn_repo_linter.mdx +++ b/api_docs/kbn_repo_linter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-linter title: "@kbn/repo-linter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-linter plugin -date: 2023-04-05 +date: 2023-04-06 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 d977b13a39d0d..c789dbe4c4d0b 100644 --- a/api_docs/kbn_repo_path.mdx +++ b/api_docs/kbn_repo_path.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-path title: "@kbn/repo-path" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-path plugin -date: 2023-04-05 +date: 2023-04-06 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 a3ca34141ce67..6bf06f9a431de 100644 --- a/api_docs/kbn_repo_source_classifier.mdx +++ b/api_docs/kbn_repo_source_classifier.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-source-classifier title: "@kbn/repo-source-classifier" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-source-classifier plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index dda1f9add6c4f..e06b54c8491ac 100644 --- a/api_docs/kbn_rison.mdx +++ b/api_docs/kbn_rison.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rison title: "@kbn/rison" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rison plugin -date: 2023-04-05 +date: 2023-04-06 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 8f7c693717601..9e8b3a9cd88eb 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rule-data-utils plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_security_solution_side_nav.mdx b/api_docs/kbn_security_solution_side_nav.mdx index efd76e61fd728..6fc747fa3a3f9 100644 --- a/api_docs/kbn_security_solution_side_nav.mdx +++ b/api_docs/kbn_security_solution_side_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-side-nav title: "@kbn/security-solution-side-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-side-nav plugin -date: 2023-04-05 +date: 2023-04-06 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 f8dfb1f9d0d5d..fec3e49f36d03 100644 --- a/api_docs/kbn_security_solution_storybook_config.mdx +++ b/api_docs/kbn_security_solution_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-storybook-config title: "@kbn/security-solution-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-storybook-config plugin -date: 2023-04-05 +date: 2023-04-06 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 d8b5526ae423f..26fccc65b3b13 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_ecs.mdx b/api_docs/kbn_securitysolution_ecs.mdx index 47bbf66394da3..4aecc5e0833f1 100644 --- a/api_docs/kbn_securitysolution_ecs.mdx +++ b/api_docs/kbn_securitysolution_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-ecs title: "@kbn/securitysolution-ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-ecs plugin -date: 2023-04-05 +date: 2023-04-06 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 77f93e759677e..a39a9ccbd59d2 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-es-utils plugin -date: 2023-04-05 +date: 2023-04-06 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 24729fc9c22e0..5c47b75a993fa 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.mdx +++ b/api_docs/kbn_securitysolution_exception_list_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-exception-list-components title: "@kbn/securitysolution-exception-list-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-exception-list-components plugin -date: 2023-04-05 +date: 2023-04-06 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 20f3330bc2dc2..7418cc7f406c0 100644 --- a/api_docs/kbn_securitysolution_grouping.mdx +++ b/api_docs/kbn_securitysolution_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-grouping title: "@kbn/securitysolution-grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-grouping plugin -date: 2023-04-05 +date: 2023-04-06 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 67d24d941b5d3..a0685f891d4ff 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2023-04-05 +date: 2023-04-06 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 fba04925157cf..f1b7f6677c949 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2023-04-05 +date: 2023-04-06 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 d762ef8a7b959..842828c070657 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2023-04-05 +date: 2023-04-06 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 eebb60ba5f145..3ac310368a078 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2023-04-05 +date: 2023-04-06 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 5979d61e7321e..be558c44e2d6f 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2023-04-05 +date: 2023-04-06 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 efb6d9a5ef690..a11685eca061f 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-api plugin -date: 2023-04-05 +date: 2023-04-06 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 e187e2d7a0f2b..5212943208bd3 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-constants plugin -date: 2023-04-05 +date: 2023-04-06 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 7166ca8afa267..154e018d2db3a 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2023-04-05 +date: 2023-04-06 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 67d51960ce04a..ca07c403f43ea 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-utils plugin -date: 2023-04-05 +date: 2023-04-06 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 75a4b0a7c4d4c..4ec9ecc0ee264 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-rules plugin -date: 2023-04-05 +date: 2023-04-06 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 6b8075a6b6c4b..58dcc97dc975b 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-t-grid plugin -date: 2023-04-05 +date: 2023-04-06 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 bd75970773742..04e4598ba14e7 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-utils plugin -date: 2023-04-05 +date: 2023-04-06 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 721f0e7fccdcc..8905bd3a50a9e 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-http-tools plugin -date: 2023-04-05 +date: 2023-04-06 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 a55df8747d874..5f140cb8c461e 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index 0d781756c92ec..ee8a462f0d46a 100644 --- a/api_docs/kbn_shared_svg.mdx +++ b/api_docs/kbn_shared_svg.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-svg title: "@kbn/shared-svg" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-svg plugin -date: 2023-04-05 +date: 2023-04-06 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 832a24ea23cef..aebde25e0238f 100644 --- a/api_docs/kbn_shared_ux_avatar_solution.mdx +++ b/api_docs/kbn_shared_ux_avatar_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-solution title: "@kbn/shared-ux-avatar-solution" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-solution plugin -date: 2023-04-05 +date: 2023-04-06 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 605a6b73c5eb3..ce3d03eb6b4bf 100644 --- a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx +++ b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-user-profile-components title: "@kbn/shared-ux-avatar-user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-user-profile-components plugin -date: 2023-04-05 +date: 2023-04-06 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 e975741e08889..d2874f9708b47 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen title: "@kbn/shared-ux-button-exit-full-screen" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen plugin -date: 2023-04-05 +date: 2023-04-06 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 331fc14451952..4a14ed7f31ad1 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen-mocks title: "@kbn/shared-ux-button-exit-full-screen-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 f847f8e60bf72..437c2bd39ff81 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2023-04-05 +date: 2023-04-06 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 1d6e76671c607..08754fa608b3c 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data title: "@kbn/shared-ux-card-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2023-04-05 +date: 2023-04-06 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 c552ea2d22496..0c937569a43d4 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data-mocks title: "@kbn/shared-ux-card-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data-mocks plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data-mocks'] --- import kbnSharedUxCardNoDataMocksObj from './kbn_shared_ux_card_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_context.mdx b/api_docs/kbn_shared_ux_file_context.mdx index a58b1681ee8a0..9d27ded808f6b 100644 --- a/api_docs/kbn_shared_ux_file_context.mdx +++ b/api_docs/kbn_shared_ux_file_context.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-context title: "@kbn/shared-ux-file-context" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-context plugin -date: 2023-04-05 +date: 2023-04-06 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 895bd18614053..e54863ce0c413 100644 --- a/api_docs/kbn_shared_ux_file_image.mdx +++ b/api_docs/kbn_shared_ux_file_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image title: "@kbn/shared-ux-file-image" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image plugin -date: 2023-04-05 +date: 2023-04-06 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 f55284ccdba8b..c269641230a97 100644 --- a/api_docs/kbn_shared_ux_file_image_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_image_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image-mocks title: "@kbn/shared-ux-file-image-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 bfbbd26641758..fa67cc6419fc1 100644 --- a/api_docs/kbn_shared_ux_file_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-mocks title: "@kbn/shared-ux-file-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 70056b4e4097e..fff240a5584be 100644 --- a/api_docs/kbn_shared_ux_file_picker.mdx +++ b/api_docs/kbn_shared_ux_file_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-picker title: "@kbn/shared-ux-file-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-picker plugin -date: 2023-04-05 +date: 2023-04-06 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 39924ca0a937e..fd35e7d697cfc 100644 --- a/api_docs/kbn_shared_ux_file_types.mdx +++ b/api_docs/kbn_shared_ux_file_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-types title: "@kbn/shared-ux-file-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-types plugin -date: 2023-04-05 +date: 2023-04-06 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 4b6659c9503f8..72067c91d192c 100644 --- a/api_docs/kbn_shared_ux_file_upload.mdx +++ b/api_docs/kbn_shared_ux_file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-upload title: "@kbn/shared-ux-file-upload" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-upload plugin -date: 2023-04-05 +date: 2023-04-06 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 b763b9e5a48bc..33f66c6c3da96 100644 --- a/api_docs/kbn_shared_ux_file_util.mdx +++ b/api_docs/kbn_shared_ux_file_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-util title: "@kbn/shared-ux-file-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-util plugin -date: 2023-04-05 +date: 2023-04-06 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 65d168723ce06..cbdc94230aac5 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app title: "@kbn/shared-ux-link-redirect-app" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app plugin -date: 2023-04-05 +date: 2023-04-06 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 acdab74162030..5a90875f126f3 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app-mocks title: "@kbn/shared-ux-link-redirect-app-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 9102cc4ab63bb..6dfa27cb87efe 100644 --- a/api_docs/kbn_shared_ux_markdown.mdx +++ b/api_docs/kbn_shared_ux_markdown.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown title: "@kbn/shared-ux-markdown" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown plugin -date: 2023-04-05 +date: 2023-04-06 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 f2dcad94666b2..04e02f7bb6465 100644 --- a/api_docs/kbn_shared_ux_markdown_mocks.mdx +++ b/api_docs/kbn_shared_ux_markdown_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown-mocks title: "@kbn/shared-ux-markdown-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 ee219c9ada501..cacd1106cce75 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data title: "@kbn/shared-ux-page-analytics-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2023-04-05 +date: 2023-04-06 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 527582657624d..34ddf8322a69e 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data-mocks title: "@kbn/shared-ux-page-analytics-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 f4216087ab411..7066f7386310a 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data title: "@kbn/shared-ux-page-kibana-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2023-04-05 +date: 2023-04-06 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 078f86761fd94..3c6bbf17ed5ff 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data-mocks title: "@kbn/shared-ux-page-kibana-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 1c331f1ecb3b0..f27b477b4dcc4 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template title: "@kbn/shared-ux-page-kibana-template" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template plugin -date: 2023-04-05 +date: 2023-04-06 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 19a27816ada7a..9a2b9c8ed8c49 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template-mocks title: "@kbn/shared-ux-page-kibana-template-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 11e9d7d6eabdd..6c510c4922cfa 100644 --- a/api_docs/kbn_shared_ux_page_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data title: "@kbn/shared-ux-page-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data plugin -date: 2023-04-05 +date: 2023-04-06 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 457475024c407..73637ca3f0ac4 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config title: "@kbn/shared-ux-page-no-data-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config plugin -date: 2023-04-05 +date: 2023-04-06 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 515e744cd7c2f..ed51b5b50583e 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config-mocks title: "@kbn/shared-ux-page-no-data-config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 efc5400440981..1caea57a3e956 100644 --- a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-mocks title: "@kbn/shared-ux-page-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 5e6e09b5a01f0..e90d2c31aec1a 100644 --- a/api_docs/kbn_shared_ux_page_solution_nav.mdx +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-solution-nav title: "@kbn/shared-ux-page-solution-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-solution-nav plugin -date: 2023-04-05 +date: 2023-04-06 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 1c0ae96de4aee..08bca4a6c3caa 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views title: "@kbn/shared-ux-prompt-no-data-views" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2023-04-05 +date: 2023-04-06 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 5d080c4cf57e7..be16b9addd42c 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views-mocks title: "@kbn/shared-ux-prompt-no-data-views-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 ab6c17e2d2202..0c7f560e8da4a 100644 --- a/api_docs/kbn_shared_ux_prompt_not_found.mdx +++ b/api_docs/kbn_shared_ux_prompt_not_found.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-not-found title: "@kbn/shared-ux-prompt-not-found" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-not-found plugin -date: 2023-04-05 +date: 2023-04-06 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 232f6930e5c60..cdc1ec346f84b 100644 --- a/api_docs/kbn_shared_ux_router.mdx +++ b/api_docs/kbn_shared_ux_router.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router title: "@kbn/shared-ux-router" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router plugin -date: 2023-04-05 +date: 2023-04-06 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 ea6f330d1d3a8..6e7588ce086be 100644 --- a/api_docs/kbn_shared_ux_router_mocks.mdx +++ b/api_docs/kbn_shared_ux_router_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router-mocks title: "@kbn/shared-ux-router-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router-mocks plugin -date: 2023-04-05 +date: 2023-04-06 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 76e6c1f7fb84e..515b4d96eb0ec 100644 --- a/api_docs/kbn_shared_ux_storybook_config.mdx +++ b/api_docs/kbn_shared_ux_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-config title: "@kbn/shared-ux-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-config plugin -date: 2023-04-05 +date: 2023-04-06 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 8b252f5672af0..882fab08b928b 100644 --- a/api_docs/kbn_shared_ux_storybook_mock.mdx +++ b/api_docs/kbn_shared_ux_storybook_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-mock title: "@kbn/shared-ux-storybook-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-mock plugin -date: 2023-04-05 +date: 2023-04-06 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 99a969cfec367..86caec6fe5008 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-utility plugin -date: 2023-04-05 +date: 2023-04-06 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 eb8f596e38265..4041f6a0b1de4 100644 --- a/api_docs/kbn_slo_schema.mdx +++ b/api_docs/kbn_slo_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-slo-schema title: "@kbn/slo-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/slo-schema plugin -date: 2023-04-05 +date: 2023-04-06 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 c83baad87238c..cb0fc3521e33d 100644 --- a/api_docs/kbn_some_dev_log.mdx +++ b/api_docs/kbn_some_dev_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-some-dev-log title: "@kbn/some-dev-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/some-dev-log plugin -date: 2023-04-05 +date: 2023-04-06 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 8450d3639d9d0..b419939793890 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/std plugin -date: 2023-04-05 +date: 2023-04-06 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 0b14c1f8feba0..625a8691f4c83 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/stdio-dev-helpers plugin -date: 2023-04-05 +date: 2023-04-06 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 bcb03dd751deb..f03882f3b5e7f 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/storybook plugin -date: 2023-04-05 +date: 2023-04-06 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 055324fde12c8..38f60712ff0a7 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/telemetry-tools plugin -date: 2023-04-05 +date: 2023-04-06 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 3bfb7925db88e..c1a3ccb786932 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test plugin -date: 2023-04-05 +date: 2023-04-06 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 205229b5c0177..5edb5e5a1b39c 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-jest-helpers plugin -date: 2023-04-05 +date: 2023-04-06 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 7810bb8aa56a8..a0e68484406c4 100644 --- a/api_docs/kbn_test_subj_selector.mdx +++ b/api_docs/kbn_test_subj_selector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-subj-selector title: "@kbn/test-subj-selector" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-subj-selector plugin -date: 2023-04-05 +date: 2023-04-06 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 33085969c677b..9d2ae19d6667a 100644 --- a/api_docs/kbn_tooling_log.mdx +++ b/api_docs/kbn_tooling_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log title: "@kbn/tooling-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/tooling-log plugin -date: 2023-04-05 +date: 2023-04-06 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 9a116416e5946..f94fc20d3474f 100644 --- a/api_docs/kbn_ts_projects.mdx +++ b/api_docs/kbn_ts_projects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ts-projects title: "@kbn/ts-projects" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ts-projects plugin -date: 2023-04-05 +date: 2023-04-06 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 3022bd019e1f1..15c150c48dc25 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/typed-react-router-config plugin -date: 2023-04-05 +date: 2023-04-06 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 92384ef157c89..a2c591e56fc19 100644 --- a/api_docs/kbn_ui_actions_browser.mdx +++ b/api_docs/kbn_ui_actions_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-actions-browser title: "@kbn/ui-actions-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-actions-browser plugin -date: 2023-04-05 +date: 2023-04-06 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 b7948c27ce9b7..7d1c280be7884 100644 --- a/api_docs/kbn_ui_shared_deps_src.mdx +++ b/api_docs/kbn_ui_shared_deps_src.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-shared-deps-src title: "@kbn/ui-shared-deps-src" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-shared-deps-src plugin -date: 2023-04-05 +date: 2023-04-06 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 eb5e8be8f9ded..03b684b703857 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-theme plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index 78c7f18f066af..8e9e0cd7f842c 100644 --- a/api_docs/kbn_user_profile_components.mdx +++ b/api_docs/kbn_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-user-profile-components title: "@kbn/user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/user-profile-components plugin -date: 2023-04-05 +date: 2023-04-06 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 8247396f71332..7599a4a42b091 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types plugin -date: 2023-04-05 +date: 2023-04-06 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 66d46eb9c84be..a1ed77dcf13e7 100644 --- a/api_docs/kbn_utility_types_jest.mdx +++ b/api_docs/kbn_utility_types_jest.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest title: "@kbn/utility-types-jest" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types-jest plugin -date: 2023-04-05 +date: 2023-04-06 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 0e2467bc87701..139430dbd0173 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utils plugin -date: 2023-04-05 +date: 2023-04-06 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 9ba553add5999..1f9877b735c6d 100644 --- a/api_docs/kbn_yarn_lock_validator.mdx +++ b/api_docs/kbn_yarn_lock_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-yarn-lock-validator title: "@kbn/yarn-lock-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/yarn-lock-validator plugin -date: 2023-04-05 +date: 2023-04-06 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 bab787718f719..ea95488582977 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaOverview plugin -date: 2023-04-05 +date: 2023-04-06 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 0487b57a93af5..1378db0fc3429 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaReact plugin -date: 2023-04-05 +date: 2023-04-06 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 8d6afc82d2e4b..d8d4e1f401f46 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaUtils plugin -date: 2023-04-05 +date: 2023-04-06 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 1d81654b24f79..10d0173112435 100644 --- a/api_docs/kubernetes_security.mdx +++ b/api_docs/kubernetes_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kubernetesSecurity title: "kubernetesSecurity" image: https://source.unsplash.com/400x175/?github description: API docs for the kubernetesSecurity plugin -date: 2023-04-05 +date: 2023-04-06 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 bfe5d0918946f..50328c06b2859 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lens title: "lens" image: https://source.unsplash.com/400x175/?github description: API docs for the lens plugin -date: 2023-04-05 +date: 2023-04-06 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 3dcfc1ac2a6ee..9b6c1b122173f 100644 --- a/api_docs/license_api_guard.mdx +++ b/api_docs/license_api_guard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard title: "licenseApiGuard" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseApiGuard plugin -date: 2023-04-05 +date: 2023-04-06 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 39c82f99eb54e..82a93cc58b604 100644 --- a/api_docs/license_management.mdx +++ b/api_docs/license_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseManagement title: "licenseManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseManagement plugin -date: 2023-04-05 +date: 2023-04-06 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 a6b6bca2e704e..01dcc3e0a1102 100644 --- a/api_docs/licensing.mdx +++ b/api_docs/licensing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licensing title: "licensing" image: https://source.unsplash.com/400x175/?github description: API docs for the licensing plugin -date: 2023-04-05 +date: 2023-04-06 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 331d57740af39..a85f4000168b0 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lists title: "lists" image: https://source.unsplash.com/400x175/?github description: API docs for the lists plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index f61b9a745be13..1700b5b2921bb 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github description: API docs for the management plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'management'] --- import managementObj from './management.devdocs.json'; diff --git a/api_docs/maps.mdx b/api_docs/maps.mdx index bd948612dd8a6..b8c5d58b4970d 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github description: API docs for the maps plugin -date: 2023-04-05 +date: 2023-04-06 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 18afc86324bbf..f84c03da5412d 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github description: API docs for the mapsEms plugin -date: 2023-04-05 +date: 2023-04-06 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 6d43e7556ffc8..b029bb9e7f503 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github description: API docs for the ml plugin -date: 2023-04-05 +date: 2023-04-06 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 122886d44eb1d..a9793ced2e3bf 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoring plugin -date: 2023-04-05 +date: 2023-04-06 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 daaa5eddda485..9e2b6941fe2d2 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoringCollection plugin -date: 2023-04-05 +date: 2023-04-06 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 2ccc4b36d8395..1aff16594dae9 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the navigation plugin -date: 2023-04-05 +date: 2023-04-06 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 13920b3b6a011..83bd153a34d8e 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github description: API docs for the newsfeed plugin -date: 2023-04-05 +date: 2023-04-06 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 d62565e6ac044..fe5c5d81e3183 100644 --- a/api_docs/notifications.mdx +++ b/api_docs/notifications.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/notifications title: "notifications" image: https://source.unsplash.com/400x175/?github description: API docs for the notifications plugin -date: 2023-04-05 +date: 2023-04-06 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 21fe9896195e7..cfbfd57b7e67d 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github description: API docs for the observability plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index ef96976d625eb..17c30c5d84274 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github description: API docs for the osquery plugin -date: 2023-04-05 +date: 2023-04-06 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 8f3b2a149bd29..8cd3fe9f06250 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -7,7 +7,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory description: Directory of public APIs available through plugins or packages. -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -21,7 +21,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 68655 | 525 | 59300 | 1323 | +| 68650 | 525 | 59305 | 1322 | ## Plugin Directory @@ -34,7 +34,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/apm-ui](https://github.com/orgs/elastic/teams/apm-ui) | The user interface for Elastic APM | 42 | 0 | 42 | 108 | | | [@elastic/infra-monitoring-ui](https://github.com/orgs/elastic/teams/infra-monitoring-ui) | Asset manager plugin for entity assets (inventory, topology, etc) | 3 | 0 | 3 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 9 | 0 | 9 | 0 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Considering using bfetch capabilities when fetching large amounts of data. This services supports batching HTTP requests and streaming responses back. | 89 | 1 | 74 | 2 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Considering using bfetch capabilities when fetching large amounts of data. This services supports batching HTTP requests and streaming responses back. | 91 | 1 | 75 | 2 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds Canvas application to Kibana | 9 | 0 | 8 | 3 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | The Case management system in Kibana | 96 | 0 | 79 | 31 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 271 | 16 | 256 | 10 | @@ -49,7 +49,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-cloud-security-posture](https://github.com/orgs/elastic/teams/kibana-cloud-security-posture) | The cloud security posture plugin | 17 | 0 | 2 | 2 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 13 | 0 | 13 | 1 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Content management app | 130 | 0 | 112 | 5 | -| | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Controls Plugin contains embeddable components intended to create a simple query interface for end users, and a powerful editing suite that allows dashboard authors to build controls | 273 | 0 | 269 | 11 | +| | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Controls Plugin contains embeddable components intended to create a simple query interface for end users, and a powerful editing suite that allows dashboard authors to build controls | 273 | 0 | 269 | 10 | | crossClusterReplication | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 0 | 0 | 0 | 0 | | customBranding | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Enables customization of Kibana | 0 | 0 | 0 | 0 | | | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | Add custom data integrations so they can be displayed in the Fleet integrations app | 271 | 0 | 252 | 1 | @@ -149,7 +149,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-reporting-services](https://github.com/orgs/elastic/teams/kibana-reporting-services) | Kibana Screenshotting Plugin | 27 | 0 | 8 | 4 | | searchprofiler | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 0 | 0 | 0 | 0 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides authentication and authorization features, and exposes functionality to understand the capabilities of the currently authenticated user. | 280 | 0 | 94 | 0 | -| | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 118 | 0 | 77 | 28 | +| | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 117 | 0 | 76 | 27 | | | [@elastic/awp-viz](https://github.com/orgs/elastic/teams/awp-viz) | - | 7 | 0 | 7 | 1 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Adds URL Service and sharing capabilities to Kibana | 118 | 0 | 59 | 10 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 22 | 1 | 22 | 1 | @@ -170,7 +170,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Adds UI Actions service to Kibana | 134 | 2 | 92 | 9 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Extends UI Actions plugin with more functionality | 206 | 0 | 140 | 9 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Contains functionality for the field list which can be integrated into apps | 276 | 0 | 250 | 7 | -| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | The `unifiedHistogram` plugin provides UI components to create a layout including a resizable histogram and a main display. | 64 | 0 | 24 | 1 | +| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | The `unifiedHistogram` plugin provides UI components to create a layout including a resizable histogram and a main display. | 52 | 0 | 23 | 2 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Contains all the key functionality of Kibana's unified search experience.Contains all the key functionality of Kibana's unified search experience. | 135 | 2 | 100 | 20 | | upgradeAssistant | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 0 | 0 | 0 | 0 | | urlDrilldown | [@elastic/kibana-app-services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds drilldown implementations to Kibana | 0 | 0 | 0 | 0 | @@ -404,7 +404,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 4 | 0 | 4 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 27 | 0 | 14 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 7 | 0 | 3 | 0 | -| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 251 | 1 | 193 | 15 | +| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 255 | 1 | 197 | 15 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 12 | 0 | 12 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 1 | 0 | | | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 13 | 0 | 4 | 3 | @@ -445,7 +445,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 31 | 1 | 24 | 1 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 71 | 0 | 69 | 3 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 53 | 1 | 48 | 0 | -| | [@elastic/actionable-observability](https://github.com/orgs/elastic/teams/actionable-observability) | - | 5 | 0 | 5 | 1 | +| | [@elastic/actionable-observability](https://github.com/orgs/elastic/teams/actionable-observability) | - | 7 | 0 | 7 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 45 | 0 | 45 | 10 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 51 | 5 | 34 | 0 | | | [@elastic/security-asset-management](https://github.com/orgs/elastic/teams/security-asset-management) | - | 62 | 0 | 62 | 0 | diff --git a/api_docs/presentation_util.devdocs.json b/api_docs/presentation_util.devdocs.json index 6a74ca3a8e3c8..f042a9523bd8d 100644 --- a/api_docs/presentation_util.devdocs.json +++ b/api_docs/presentation_util.devdocs.json @@ -732,9 +732,9 @@ }, "[]; selectedDataViewId?: string | undefined; trigger: ", "DataViewTriggerProps", - "; onChangeDataViewId: (newId: string) => void; selectableProps?: ", + "; onChangeDataViewId: (newId: string) => void; selectableProps?: Partial<", "EuiSelectableProps", - "<{}> | undefined; }> & { readonly _result: ({ dataViews, selectedDataViewId, onChangeDataViewId, trigger, selectableProps, }: { dataViews: ", + "<{}>> | undefined; }> & { readonly _result: ({ dataViews, selectedDataViewId, onChangeDataViewId, trigger, selectableProps, }: { dataViews: ", { "pluginId": "dataViews", "scope": "common", @@ -744,9 +744,9 @@ }, "[]; selectedDataViewId?: string | undefined; trigger: ", "DataViewTriggerProps", - "; onChangeDataViewId: (newId: string) => void; selectableProps?: ", + "; onChangeDataViewId: (newId: string) => void; selectableProps?: Partial<", "EuiSelectableProps", - "<{}> | undefined; }) => JSX.Element; }" + "<{}>> | undefined; }) => JSX.Element; }" ], "path": "src/plugins/presentation_util/public/components/index.tsx", "deprecated": false, @@ -830,7 +830,7 @@ "signature": [ "React.ExoticComponent<", "FieldPickerProps", - "> & { readonly _result: ({ dataView, onSelectField, filterPredicate, selectedFieldName, }: ", + "> & { readonly _result: ({ dataView, onSelectField, filterPredicate, selectedFieldName, selectableProps, }: ", "FieldPickerProps", ") => JSX.Element; }" ], diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index a038c28c2371f..bfb1747ee27e3 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationUtil plugin -date: 2023-04-05 +date: 2023-04-06 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 ec3a12ed837f4..4dfc59501e5a2 100644 --- a/api_docs/profiling.mdx +++ b/api_docs/profiling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profiling title: "profiling" image: https://source.unsplash.com/400x175/?github description: API docs for the profiling plugin -date: 2023-04-05 +date: 2023-04-06 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 cfa30edb3d192..c23a8a8dcafd4 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github description: API docs for the remoteClusters plugin -date: 2023-04-05 +date: 2023-04-06 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 f95418cce3901..3983a4f5a3adf 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github description: API docs for the reporting plugin -date: 2023-04-05 +date: 2023-04-06 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 2d6cfb6090ba1..efb630703231b 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the rollup plugin -date: 2023-04-05 +date: 2023-04-06 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 281f8000faaa2..de2abf6b5b842 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github description: API docs for the ruleRegistry plugin -date: 2023-04-05 +date: 2023-04-06 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 7056673d3e6f7..cbaed33ddaadf 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github description: API docs for the runtimeFields plugin -date: 2023-04-05 +date: 2023-04-06 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 d696da187cdb8..a8cd818da91ba 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjects plugin -date: 2023-04-05 +date: 2023-04-06 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 0f979cd9d54ad..3544b01de620f 100644 --- a/api_docs/saved_objects_finder.mdx +++ b/api_docs/saved_objects_finder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsFinder title: "savedObjectsFinder" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsFinder plugin -date: 2023-04-05 +date: 2023-04-06 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 0e81058fab93c..7ed97709fed2b 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsManagement plugin -date: 2023-04-05 +date: 2023-04-06 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 59f4403dc9ab7..361eea1872d62 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTagging plugin -date: 2023-04-05 +date: 2023-04-06 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 9ea4424f15b46..d0b2f8a92df6f 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTaggingOss plugin -date: 2023-04-05 +date: 2023-04-06 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 46c4115ff2e3c..53bcbe8206768 100644 --- a/api_docs/saved_search.mdx +++ b/api_docs/saved_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedSearch title: "savedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the savedSearch plugin -date: 2023-04-05 +date: 2023-04-06 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 0202e76f72692..7123ac554f720 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotMode plugin -date: 2023-04-05 +date: 2023-04-06 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 7b9e27f3959ff..47654a23ecaa7 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotting plugin -date: 2023-04-05 +date: 2023-04-06 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 62be24d045abb..e61fddcb0ac25 100644 --- a/api_docs/security.mdx +++ b/api_docs/security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/security title: "security" image: https://source.unsplash.com/400x175/?github description: API docs for the security plugin -date: 2023-04-05 +date: 2023-04-06 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 2671b393b725f..3c468e74d4b85 100644 --- a/api_docs/security_solution.devdocs.json +++ b/api_docs/security_solution.devdocs.json @@ -2023,20 +2023,6 @@ "trackAdoption": false, "children": [], "returnComment": [] - }, - { - "parentPluginId": "securitySolution", - "id": "def-server.SecuritySolutionApiRequestHandlerContext.getQueryRuleAdditionalOptions", - "type": "Object", - "tags": [], - "label": "getQueryRuleAdditionalOptions", - "description": [], - "signature": [ - "CreateQueryRuleAdditionalOptions" - ], - "path": "x-pack/plugins/security_solution/server/types.ts", - "deprecated": false, - "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index 941b0adb05ab1..cc2d65d33e36b 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolution title: "securitySolution" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolution plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-solution](https://github.com/orgs/elastic/teams/secur | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 118 | 0 | 77 | 28 | +| 117 | 0 | 76 | 27 | ## Client diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index b234e977c21af..143e34b65b40d 100644 --- a/api_docs/session_view.mdx +++ b/api_docs/session_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/sessionView title: "sessionView" image: https://source.unsplash.com/400x175/?github description: API docs for the sessionView plugin -date: 2023-04-05 +date: 2023-04-06 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 cd9c0d4ef98d2..30067c2c2945d 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/share title: "share" image: https://source.unsplash.com/400x175/?github description: API docs for the share plugin -date: 2023-04-05 +date: 2023-04-06 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 d036a16a544c7..b24625cca9b87 100644 --- a/api_docs/snapshot_restore.mdx +++ b/api_docs/snapshot_restore.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/snapshotRestore title: "snapshotRestore" image: https://source.unsplash.com/400x175/?github description: API docs for the snapshotRestore plugin -date: 2023-04-05 +date: 2023-04-06 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 88f2af5705f1f..f3838fd3629d2 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/spaces title: "spaces" image: https://source.unsplash.com/400x175/?github description: API docs for the spaces plugin -date: 2023-04-05 +date: 2023-04-06 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 c8f68bffba413..b690e769578fe 100644 --- a/api_docs/stack_alerts.mdx +++ b/api_docs/stack_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackAlerts title: "stackAlerts" image: https://source.unsplash.com/400x175/?github description: API docs for the stackAlerts plugin -date: 2023-04-05 +date: 2023-04-06 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 e818a2dc9219b..2c031b1c8798d 100644 --- a/api_docs/stack_connectors.mdx +++ b/api_docs/stack_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackConnectors title: "stackConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the stackConnectors plugin -date: 2023-04-05 +date: 2023-04-06 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 23a77aac5ffef..4a1394bd405cc 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/taskManager title: "taskManager" image: https://source.unsplash.com/400x175/?github description: API docs for the taskManager plugin -date: 2023-04-05 +date: 2023-04-06 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 6ba3935636905..3ded97e27272e 100644 --- a/api_docs/telemetry.mdx +++ b/api_docs/telemetry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetry title: "telemetry" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetry plugin -date: 2023-04-05 +date: 2023-04-06 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 72665d3c69d70..fcc4c0a8920d1 100644 --- a/api_docs/telemetry_collection_manager.mdx +++ b/api_docs/telemetry_collection_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager title: "telemetryCollectionManager" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionManager plugin -date: 2023-04-05 +date: 2023-04-06 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 31a39aa33042d..bd0ac9bcd71ef 100644 --- a/api_docs/telemetry_collection_xpack.mdx +++ b/api_docs/telemetry_collection_xpack.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionXpack title: "telemetryCollectionXpack" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionXpack plugin -date: 2023-04-05 +date: 2023-04-06 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 5885cca5b8849..c4d18b27b2aeb 100644 --- a/api_docs/telemetry_management_section.mdx +++ b/api_docs/telemetry_management_section.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection title: "telemetryManagementSection" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryManagementSection plugin -date: 2023-04-05 +date: 2023-04-06 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 6db1b3954afc6..d982a4f82e906 100644 --- a/api_docs/threat_intelligence.mdx +++ b/api_docs/threat_intelligence.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/threatIntelligence title: "threatIntelligence" image: https://source.unsplash.com/400x175/?github description: API docs for the threatIntelligence plugin -date: 2023-04-05 +date: 2023-04-06 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 0a65a5573f15b..a00be5bcd5a69 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github description: API docs for the timelines plugin -date: 2023-04-05 +date: 2023-04-06 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 c613503a18cd8..fed6b815104e5 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github description: API docs for the transform plugin -date: 2023-04-05 +date: 2023-04-06 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 147fe60639e7f..50bf64ae88a48 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github description: API docs for the triggersActionsUi plugin -date: 2023-04-05 +date: 2023-04-06 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 fb00166312b56..860ed34912743 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActions plugin -date: 2023-04-05 +date: 2023-04-06 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 97f06f2a715a4..2c79102b5b43a 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActionsEnhanced plugin -date: 2023-04-05 +date: 2023-04-06 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 ca238a0986b2c..30a8e59ef89d9 100644 --- a/api_docs/unified_field_list.mdx +++ b/api_docs/unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedFieldList title: "unifiedFieldList" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedFieldList plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedFieldList'] --- import unifiedFieldListObj from './unified_field_list.devdocs.json'; diff --git a/api_docs/unified_histogram.devdocs.json b/api_docs/unified_histogram.devdocs.json index f1cd83669244f..f88fd244420f4 100644 --- a/api_docs/unified_histogram.devdocs.json +++ b/api_docs/unified_histogram.devdocs.json @@ -439,18 +439,36 @@ "tags": [], "label": "UnifiedHistogramContainer", "description": [ - "\nA resizable layout component with two panels that renders a histogram with a hits\ncounter in the top panel, and a main display (data table, etc.) in the bottom panel.\nIf all context props are left undefined, the layout will render in a single panel\nmode including only the main display." + "\nA resizable layout component with two panels that renders a histogram with a hits\ncounter in the top panel, and a main display (data table, etc.) in the bottom panel." ], "signature": [ - "React.ForwardRefExoticComponent ", { "pluginId": "unifiedHistogram", "scope": "public", "docId": "kibUnifiedHistogramPluginApi", - "section": "def-public.UnifiedHistogramContainerProps", - "text": "UnifiedHistogramContainerProps" + "section": "def-public.UnifiedHistogramCreationOptions", + "text": "UnifiedHistogramCreationOptions" }, - " & React.RefAttributes<", + " | Promise<", + { + "pluginId": "unifiedHistogram", + "scope": "public", + "docId": "kibUnifiedHistogramPluginApi", + "section": "def-public.UnifiedHistogramCreationOptions", + "text": "UnifiedHistogramCreationOptions" + }, + ">) | undefined; searchSessionId?: string | undefined; requestAdapter?: ", + { + "pluginId": "inspector", + "scope": "common", + "docId": "kibInspectorPluginApi", + "section": "def-common.RequestAdapter", + "text": "RequestAdapter" + }, + " | undefined; } & Pick<", + "UnifiedHistogramLayoutProps", + ", \"children\" | \"className\" | \"query\" | \"filters\" | \"columns\" | \"timeRange\" | \"services\" | \"dataView\" | \"relativeTimeRange\" | \"resizeRef\" | \"appendHitsCounter\"> & React.RefAttributes<", { "pluginId": "unifiedHistogram", "scope": "public", @@ -458,7 +476,7 @@ "section": "def-public.UnifiedHistogramApi", "text": "UnifiedHistogramApi" }, - ">, \"children\" | \"className\" | \"css\" | \"key\" | \"resizeRef\" | \"appendHitsCounter\"> & React.RefAttributes<{}>>" + ">>" ], "path": "src/plugins/unified_histogram/public/container/index.tsx", "deprecated": false, @@ -833,22 +851,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "unifiedHistogram", - "id": "def-public.UnifiedHistogramState.columns", - "type": "Array", - "tags": [], - "label": "columns", - "description": [ - "\nThe current selected columns" - ], - "signature": [ - "string[] | undefined" - ], - "path": "src/plugins/unified_histogram/public/container/services/state_service.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "unifiedHistogram", "id": "def-public.UnifiedHistogramState.currentSuggestion", @@ -885,51 +887,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "unifiedHistogram", - "id": "def-public.UnifiedHistogramState.dataView", - "type": "Object", - "tags": [], - "label": "dataView", - "description": [ - "\nThe current data view" - ], - "signature": [ - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataView", - "text": "DataView" - } - ], - "path": "src/plugins/unified_histogram/public/container/services/state_service.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "unifiedHistogram", - "id": "def-public.UnifiedHistogramState.filters", - "type": "Array", - "tags": [], - "label": "filters", - "description": [ - "\nThe current filters" - ], - "signature": [ - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.Filter", - "text": "Filter" - }, - "[]" - ], - "path": "src/plugins/unified_histogram/public/container/services/state_service.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "unifiedHistogram", "id": "def-public.UnifiedHistogramState.lensRequestAdapter", @@ -953,75 +910,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "unifiedHistogram", - "id": "def-public.UnifiedHistogramState.query", - "type": "CompoundType", - "tags": [], - "label": "query", - "description": [ - "\nThe current query" - ], - "signature": [ - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.Query", - "text": "Query" - }, - " | ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.AggregateQuery", - "text": "AggregateQuery" - } - ], - "path": "src/plugins/unified_histogram/public/container/services/state_service.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "unifiedHistogram", - "id": "def-public.UnifiedHistogramState.requestAdapter", - "type": "Object", - "tags": [], - "label": "requestAdapter", - "description": [ - "\nThe current request adapter used for non-Lens requests" - ], - "signature": [ - { - "pluginId": "inspector", - "scope": "common", - "docId": "kibInspectorPluginApi", - "section": "def-common.RequestAdapter", - "text": "RequestAdapter" - }, - " | undefined" - ], - "path": "src/plugins/unified_histogram/public/container/services/state_service.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "unifiedHistogram", - "id": "def-public.UnifiedHistogramState.searchSessionId", - "type": "string", - "tags": [], - "label": "searchSessionId", - "description": [ - "\nThe current search session ID" - ], - "signature": [ - "string | undefined" - ], - "path": "src/plugins/unified_histogram/public/container/services/state_service.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "unifiedHistogram", "id": "def-public.UnifiedHistogramState.timeInterval", @@ -1035,22 +923,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "unifiedHistogram", - "id": "def-public.UnifiedHistogramState.timeRange", - "type": "Object", - "tags": [], - "label": "timeRange", - "description": [ - "\nThe current time range" - ], - "signature": [ - "{ from: string; to: string; mode?: \"absolute\" | \"relative\" | undefined; }" - ], - "path": "src/plugins/unified_histogram/public/container/services/state_service.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "unifiedHistogram", "id": "def-public.UnifiedHistogramState.topPanelHeight", @@ -1162,7 +1034,7 @@ { "parentPluginId": "unifiedHistogram", "id": "def-public.UnifiedHistogramStateOptions.initialState", - "type": "CompoundType", + "type": "Object", "tags": [], "label": "initialState", "description": [ @@ -1177,15 +1049,7 @@ "section": "def-public.UnifiedHistogramState", "text": "UnifiedHistogramState" }, - "> & Pick<", - { - "pluginId": "unifiedHistogram", - "scope": "public", - "docId": "kibUnifiedHistogramPluginApi", - "section": "def-public.UnifiedHistogramState", - "text": "UnifiedHistogramState" - }, - ", \"dataView\">" + "> | undefined" ], "path": "src/plugins/unified_histogram/public/container/services/state_service.ts", "deprecated": false, @@ -1193,86 +1057,6 @@ } ], "initialIsOpen": false - }, - { - "parentPluginId": "unifiedHistogram", - "id": "def-public.UnifiedHistogramUninitializedApi", - "type": "Interface", - "tags": [], - "label": "UnifiedHistogramUninitializedApi", - "description": [ - "\nThe uninitialized API exposed by the container" - ], - "path": "src/plugins/unified_histogram/public/container/container.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "unifiedHistogram", - "id": "def-public.UnifiedHistogramUninitializedApi.initialized", - "type": "boolean", - "tags": [], - "label": "initialized", - "description": [ - "\nWhether the container has been initialized" - ], - "signature": [ - "false" - ], - "path": "src/plugins/unified_histogram/public/container/container.tsx", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "unifiedHistogram", - "id": "def-public.UnifiedHistogramUninitializedApi.initialize", - "type": "Function", - "tags": [], - "label": "initialize", - "description": [ - "\nInitialize the container" - ], - "signature": [ - "(options: ", - { - "pluginId": "unifiedHistogram", - "scope": "public", - "docId": "kibUnifiedHistogramPluginApi", - "section": "def-public.UnifiedHistogramInitializeOptions", - "text": "UnifiedHistogramInitializeOptions" - }, - ") => void" - ], - "path": "src/plugins/unified_histogram/public/container/container.tsx", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "unifiedHistogram", - "id": "def-public.UnifiedHistogramUninitializedApi.initialize.$1", - "type": "CompoundType", - "tags": [], - "label": "options", - "description": [], - "signature": [ - { - "pluginId": "unifiedHistogram", - "scope": "public", - "docId": "kibUnifiedHistogramPluginApi", - "section": "def-public.UnifiedHistogramInitializeOptions", - "text": "UnifiedHistogramInitializeOptions" - } - ], - "path": "src/plugins/unified_histogram/public/container/container.tsx", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - } - ], - "initialIsOpen": false } ], "enums": [ @@ -1343,21 +1127,9 @@ "\nThe API exposed by the container" ], "signature": [ - { - "pluginId": "unifiedHistogram", - "scope": "public", - "docId": "kibUnifiedHistogramPluginApi", - "section": "def-public.UnifiedHistogramUninitializedApi", - "text": "UnifiedHistogramUninitializedApi" - }, - " | ", - { - "pluginId": "unifiedHistogram", - "scope": "public", - "docId": "kibUnifiedHistogramPluginApi", - "section": "def-public.UnifiedHistogramInitializedApi", - "text": "UnifiedHistogramInitializedApi" - } + "{ refetch: () => void; } & Pick<", + "UnifiedHistogramStateService", + ", \"state$\" | \"setChartHidden\" | \"setTopPanelHeight\" | \"setBreakdownField\" | \"setTimeInterval\" | \"setTotalHits\">" ], "path": "src/plugins/unified_histogram/public/container/container.tsx", "deprecated": false, @@ -1374,26 +1146,33 @@ "\nThe props exposed by the container" ], "signature": [ - "{ children?: React.ReactNode; className?: string | undefined; resizeRef: React.RefObject; appendHitsCounter?: React.ReactElement> | undefined; }" - ], - "path": "src/plugins/unified_histogram/public/container/container.tsx", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "unifiedHistogram", - "id": "def-public.UnifiedHistogramInitializedApi", - "type": "Type", - "tags": [], - "label": "UnifiedHistogramInitializedApi", - "description": [ - "\nThe initialized API exposed by the container" - ], - "signature": [ - "{ initialized: true; refetch: () => void; } & Pick<", - "UnifiedHistogramStateService", - ", \"state$\" | \"setChartHidden\" | \"setTopPanelHeight\" | \"setBreakdownField\" | \"setColumns\" | \"setTimeInterval\" | \"setRequestParams\" | \"setTotalHits\">" + "{ getCreationOptions?: (() => ", + { + "pluginId": "unifiedHistogram", + "scope": "public", + "docId": "kibUnifiedHistogramPluginApi", + "section": "def-public.UnifiedHistogramCreationOptions", + "text": "UnifiedHistogramCreationOptions" + }, + " | Promise<", + { + "pluginId": "unifiedHistogram", + "scope": "public", + "docId": "kibUnifiedHistogramPluginApi", + "section": "def-public.UnifiedHistogramCreationOptions", + "text": "UnifiedHistogramCreationOptions" + }, + ">) | undefined; searchSessionId?: string | undefined; requestAdapter?: ", + { + "pluginId": "inspector", + "scope": "common", + "docId": "kibInspectorPluginApi", + "section": "def-common.RequestAdapter", + "text": "RequestAdapter" + }, + " | undefined; } & Pick<", + "UnifiedHistogramLayoutProps", + ", \"children\" | \"className\" | \"query\" | \"filters\" | \"columns\" | \"timeRange\" | \"services\" | \"dataView\" | \"relativeTimeRange\" | \"resizeRef\" | \"appendHitsCounter\">" ], "path": "src/plugins/unified_histogram/public/container/container.tsx", "deprecated": false, @@ -1402,14 +1181,15 @@ }, { "parentPluginId": "unifiedHistogram", - "id": "def-public.UnifiedHistogramInitializeOptions", + "id": "def-public.UnifiedHistogramCreationOptions", "type": "Type", "tags": [], - "label": "UnifiedHistogramInitializeOptions", + "label": "UnifiedHistogramCreationOptions", "description": [ "\nThe options used to initialize the container" ], "signature": [ + "Omit<", { "pluginId": "unifiedHistogram", "scope": "public", @@ -1417,7 +1197,7 @@ "section": "def-public.UnifiedHistogramStateOptions", "text": "UnifiedHistogramStateOptions" }, - " & Omit" + ", \"services\"> & LayoutProps" ], "path": "src/plugins/unified_histogram/public/container/container.tsx", "deprecated": false, diff --git a/api_docs/unified_histogram.mdx b/api_docs/unified_histogram.mdx index 971d6ea820b55..997d660a571f7 100644 --- a/api_docs/unified_histogram.mdx +++ b/api_docs/unified_histogram.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedHistogram title: "unifiedHistogram" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedHistogram plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedHistogram'] --- import unifiedHistogramObj from './unified_histogram.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 64 | 0 | 24 | 1 | +| 52 | 0 | 23 | 2 | ## Client diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 3ed5fd7d01075..74f5745e498f0 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch plugin -date: 2023-04-05 +date: 2023-04-06 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 d7741a9f0a395..bbeee713bbfff 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch.autocomplete plugin -date: 2023-04-05 +date: 2023-04-06 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 53d9cf442112f..a04e5a3b8ba51 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github description: API docs for the urlForwarding plugin -date: 2023-04-05 +date: 2023-04-06 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 07de7fa9dea13..bc29f3c720540 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the usageCollection plugin -date: 2023-04-05 +date: 2023-04-06 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 7c522186474fb..f408c8ff24124 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github description: API docs for the ux plugin -date: 2023-04-05 +date: 2023-04-06 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 3bdd5727554d7..ba42b1e869aef 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the visDefaultEditor plugin -date: 2023-04-05 +date: 2023-04-06 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 1f378ae8d0b27..90a3cab9cc456 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeGauge plugin -date: 2023-04-05 +date: 2023-04-06 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 db1e797e40d7d..1ade4d0aac9d9 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeHeatmap plugin -date: 2023-04-05 +date: 2023-04-06 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 51dd4ee6ef22e..e96247b4c9e3d 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypePie plugin -date: 2023-04-05 +date: 2023-04-06 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 98a76478371cb..1f15f67c78e98 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTable plugin -date: 2023-04-05 +date: 2023-04-06 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 f96970e61f4a3..f727e9b2e59b3 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimelion plugin -date: 2023-04-05 +date: 2023-04-06 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 da05db62bed1c..24146b3c9b637 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimeseries plugin -date: 2023-04-05 +date: 2023-04-06 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 f48095ea4189c..5cca8d6bb6df9 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVega plugin -date: 2023-04-05 +date: 2023-04-06 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 9962bf5cab1c2..e17c2158dddc5 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVislib plugin -date: 2023-04-05 +date: 2023-04-06 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 f64d98bc84fb7..98ab0914c9d85 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeXy plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index 981e59040eed4..965f2009fb7e4 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizations plugin -date: 2023-04-05 +date: 2023-04-06 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; diff --git a/docs/discover/save-search.asciidoc b/docs/discover/save-search.asciidoc index deede3cf2b922..10abef2e4a1bb 100644 --- a/docs/discover/save-search.asciidoc +++ b/docs/discover/save-search.asciidoc @@ -30,9 +30,21 @@ current view of *Discover*, including the columns and sort order in the document If the saved search is associated with a different {data-source} than is currently selected, opening the saved search changes the selected {data-source}. The query language used for the saved search is also automatically selected. -. To add your search results to a dashboard: -.. Open the main menu, then click *Dashboard*. -.. Open or create the dashboard, then click *Edit*. -.. Click *Add from library*. -.. From the *Types* dropdown, select *Saved search*. -.. Select the saved search that you want to visualize, then click *X* to close the list. + +[float] +=== Duplicate a search +. In **Discover**, open the search that you want to duplicate. +. In the toolbar, click *Save*. +. Give the search a new name. +. Turn on **Save as new search**. +. Click *Save*. + + +[float] +=== Add search results to a dashboard + +. Open the main menu, and then click *Dashboard*. +. Open or create the dashboard, then click *Edit*. +. Click *Add from library*. +. From the *Types* dropdown, select *Saved search*. +. Select the saved search that you want to visualize, then click *X* to close the list. diff --git a/fleet_packages.json b/fleet_packages.json index f0a38ac3b062e..a971a9dc41bdd 100644 --- a/fleet_packages.json +++ b/fleet_packages.json @@ -24,17 +24,17 @@ [ { "name": "apm", - "version": "8.8.0-preview-1677038019", + "version": "8.8.0-preview-1680182372", "forceAlignStackVersion": true, "allowSyncToPrerelease": true }, { "name": "elastic_agent", - "version": "1.5.1" + "version": "1.7.0" }, { "name": "endpoint", - "version": "8.7.0" + "version": "8.7.1" }, { "name": "fleet_server", @@ -42,7 +42,7 @@ }, { "name": "synthetics", - "version": "0.11.5" + "version": "0.11.8" }, { "name": "security_detection_engine", diff --git a/package.json b/package.json index bc2b74d165250..b4a7a8ac9632c 100644 --- a/package.json +++ b/package.json @@ -720,6 +720,7 @@ "JSONStream": "1.3.5", "abort-controller": "^3.0.0", "adm-zip": "^0.5.9", + "ajv": "^8.11.0", "antlr4ts": "^0.5.0-alpha.3", "archiver": "^5.3.1", "async": "^3.2.3", @@ -1297,7 +1298,6 @@ "@yarnpkg/lockfile": "^1.1.0", "abab": "^2.0.4", "aggregate-error": "^3.1.0", - "ajv": "^8.11.0", "antlr4ts-cli": "^0.5.0-alpha.3", "apidoc-markdown": "^7.2.4", "argsplit": "^1.0.5", diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/es_errors.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/es_errors.test.ts index f0004851b1bf9..a68cc62e76c50 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/es_errors.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/es_errors.test.ts @@ -61,6 +61,14 @@ describe('isIncompatibleMappingExceptionError', () => { }) ).toEqual(true); }); + it('returns true for `document_parsing_exception` errors', () => { + expect( + isIncompatibleMappingException({ + type: 'document_parsing_exception', + reason: 'idk', + }) + ).toEqual(true); + }); it('returns false undefined', () => { expect(isIncompatibleMappingException(undefined)).toEqual(false); }); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/es_errors.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/es_errors.ts index 5b2001f06e510..c4eeebd7df216 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/es_errors.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/es_errors.ts @@ -17,7 +17,8 @@ export const isWriteBlockException = (errorCause?: estypes.ErrorCause): boolean export const isIncompatibleMappingException = (errorCause?: estypes.ErrorCause): boolean => { return ( errorCause?.type === 'strict_dynamic_mapping_exception' || - errorCause?.type === 'mapper_parsing_exception' + errorCause?.type === 'mapper_parsing_exception' || + errorCause?.type === 'document_parsing_exception' ); }; diff --git a/packages/kbn-es-query/src/kuery/node_types/node_builder.test.ts b/packages/kbn-es-query/src/kuery/node_types/node_builder.test.ts index 141eb66b3ed58..46e21245bf333 100644 --- a/packages/kbn-es-query/src/kuery/node_types/node_builder.test.ts +++ b/packages/kbn-es-query/src/kuery/node_types/node_builder.test.ts @@ -277,4 +277,33 @@ describe('nodeBuilder', () => { `); }); }); + + describe('range method', () => { + const date = new Date(1679741259769); + const dateString = date.toISOString(); + + test('formats all range operators', () => { + const operators: Array<'gt' | 'gte' | 'lt' | 'lte'> = ['gt', 'gte', 'lt', 'lte']; + + for (const operator of operators) { + const nodes = nodeBuilder.range('foo', operator, dateString); + const query = toElasticsearchQuery(nodes); + + expect(query).toMatchObject({ + bool: { + minimum_should_match: 1, + should: [ + { + range: { + foo: { + [operator]: dateString, + }, + }, + }, + ], + }, + }); + } + }); + }); }); diff --git a/packages/kbn-es-query/src/kuery/node_types/node_builder.ts b/packages/kbn-es-query/src/kuery/node_types/node_builder.ts index a575bb2d75c67..948985c965378 100644 --- a/packages/kbn-es-query/src/kuery/node_types/node_builder.ts +++ b/packages/kbn-es-query/src/kuery/node_types/node_builder.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import type { RangeFilterParams } from '../../filters'; import { KueryNode, nodeTypes } from '../types'; export const nodeBuilder = { @@ -21,4 +22,15 @@ export const nodeBuilder = { and: (nodes: KueryNode[]): KueryNode => { return nodes.length > 1 ? nodeTypes.function.buildNode('and', nodes) : nodes[0]; }, + range: ( + fieldName: string, + operator: keyof Pick, + value: number | string + ) => { + return nodeTypes.function.buildNodeWithArgumentNodes('range', [ + nodeTypes.literal.buildNode(fieldName), + operator, + typeof value === 'string' ? nodeTypes.literal.buildNode(value) : value, + ]); + }, }; diff --git a/packages/kbn-expandable-flyout/README.md b/packages/kbn-expandable-flyout/README.md index 8d21caba73a21..8a9a201ff89af 100644 --- a/packages/kbn-expandable-flyout/README.md +++ b/packages/kbn-expandable-flyout/README.md @@ -61,6 +61,5 @@ A set of properties defining what's displayed in one of the flyout section. ## Future work -- currently the panels are aware of their width. This should be changed and the width of the left, right and preview sections should be handled by the flyout itself -- add the feature to save the flyout state (layout) to the url +- add the feature to save the flyout state (layout) to the url (https://github.com/elastic/security-team/issues/6119) - introduce the notion of scope to be able to handle more than one flyout per plugin?? \ No newline at end of file diff --git a/packages/kbn-expandable-flyout/src/components/left_section.tsx b/packages/kbn-expandable-flyout/src/components/left_section.tsx index ddf53efbad2b8..88326acb6476e 100644 --- a/packages/kbn-expandable-flyout/src/components/left_section.tsx +++ b/packages/kbn-expandable-flyout/src/components/left_section.tsx @@ -26,10 +26,8 @@ interface LeftSectionProps { */ export const LeftSection: React.FC = ({ component, width }: LeftSectionProps) => { return ( - - - {component} - + + {component} ); }; diff --git a/packages/kbn-expandable-flyout/src/components/preview_section.tsx b/packages/kbn-expandable-flyout/src/components/preview_section.tsx index e474d1204bf03..0c030293e8807 100644 --- a/packages/kbn-expandable-flyout/src/components/preview_section.tsx +++ b/packages/kbn-expandable-flyout/src/components/preview_section.tsx @@ -32,7 +32,7 @@ interface PreviewSectionProps { /** * Width used when rendering the panel */ - width: number | undefined; + width: number; /** * Display the back button in the header */ @@ -50,8 +50,7 @@ export const PreviewSection: React.FC = ({ }: PreviewSectionProps) => { const { euiTheme } = useEuiTheme(); const { closePreviewPanel, previousPreviewPanel } = useExpandableFlyoutContext(); - - const previewWith: string = width ? `${width}px` : '0px'; + const left = `${(1 - width) * 100}%`; const closeButton = ( @@ -91,7 +90,7 @@ export const PreviewSection: React.FC = ({ top: 0; bottom: 0; right: 0; - left: ${previewWith}; + left: ${left}; background-color: ${euiTheme.colors.shadow}; opacity: 0.5; `} @@ -102,7 +101,7 @@ export const PreviewSection: React.FC = ({ top: 0; bottom: 0; right: 0; - left: ${previewWith}; + left: ${left}; z-index: 1000; `} > diff --git a/packages/kbn-expandable-flyout/src/components/right_section.tsx b/packages/kbn-expandable-flyout/src/components/right_section.tsx index d018dd8721ee7..027f523a93050 100644 --- a/packages/kbn-expandable-flyout/src/components/right_section.tsx +++ b/packages/kbn-expandable-flyout/src/components/right_section.tsx @@ -29,10 +29,12 @@ export const RightSection: React.FC = ({ width, }: RightSectionProps) => { return ( - - - {component} - + + {component} ); }; diff --git a/packages/kbn-expandable-flyout/src/index.test.tsx b/packages/kbn-expandable-flyout/src/index.test.tsx index fd1d1990ffbad..2e30d5f307190 100644 --- a/packages/kbn-expandable-flyout/src/index.test.tsx +++ b/packages/kbn-expandable-flyout/src/index.test.tsx @@ -17,7 +17,6 @@ describe('ExpandableFlyout', () => { const registeredPanels: Panel[] = [ { key: 'key', - width: 500, component: () =>
{'component'}
, }, ]; diff --git a/packages/kbn-expandable-flyout/src/index.tsx b/packages/kbn-expandable-flyout/src/index.tsx index 80dd1d425f2a2..8dc44a87dab45 100644 --- a/packages/kbn-expandable-flyout/src/index.tsx +++ b/packages/kbn-expandable-flyout/src/index.tsx @@ -30,6 +30,9 @@ export interface ExpandableFlyoutProps extends EuiFlyoutProps { /** * Expandable flyout UI React component. * Displays 3 sections (right, left, preview) depending on the panels in the context. + * + * The behavior expects that the left and preview sections should only be displayed is a right section + * is already rendered. */ export const ExpandableFlyout: React.FC = ({ registeredPanels, @@ -67,7 +70,10 @@ export const ExpandableFlyout: React.FC = ({ return <>; } - const width: number = (leftSection?.width ?? 0) + (rightSection?.width ?? 0); + const flyoutWidth: string = leftSection && rightSection ? 'l' : 's'; + const rightSectionWidth: number = leftSection ? 0.4 : 1; + const leftSectionWidth: number = 0.6; + const previewSectionWidth: number = leftSection ? 0.4 : 1; return ( = ({ overflow-y: scroll; `} {...flyoutProps} - size={width} + size={flyoutWidth} ownFocus={false} onClose={onClose} > @@ -88,13 +94,13 @@ export const ExpandableFlyout: React.FC = ({ {leftSection && left ? ( ) : null} {rightSection && right ? ( ) : null} @@ -103,7 +109,7 @@ export const ExpandableFlyout: React.FC = ({ ) : null} diff --git a/packages/kbn-expandable-flyout/src/types.ts b/packages/kbn-expandable-flyout/src/types.ts index 2413fbb56ca7d..b27ac4afd6fc2 100644 --- a/packages/kbn-expandable-flyout/src/types.ts +++ b/packages/kbn-expandable-flyout/src/types.ts @@ -36,8 +36,4 @@ export interface Panel { * Component to be rendered */ component: (props: FlyoutPanel) => React.ReactElement; - /** - * Width used when rendering the panel - */ - width: number; // TODO remove this, see https://github.com/elastic/security-team/issues/6247 } diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts index caa6d371d50b2..86198df9b1d27 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts @@ -108,7 +108,7 @@ describe('checking migration metadata changes on all registered SO types', () => "ingest-package-policies": "6dc1c9b80a8dc95fbc9c6d9b73dfc56a098eb440", "ingest_manager_settings": "fb75bff08a8de3435b23664b1191f9244a255701", "inventory-view": "6d47ef0b38166ecbd1c2fc7394599a4500db1ae4", - "kql-telemetry": "23ed96ff02cd69cbfaa22f313cae3a54c434db51", + "kql-telemetry": "92d6357aa3ce28727492f86a54783f802dc38893", "legacy-url-alias": "9b8cca3fbb2da46fd12823d3cd38fdf1c9f24bc8", "lens": "2f6a8231591e3d62a83506b19e165774d74588ea", "lens-ui-telemetry": "d6c4e330d170eefc6214dbf77a53de913fa3eebc", @@ -127,7 +127,7 @@ describe('checking migration metadata changes on all registered SO types', () => "sample-data-telemetry": "c38daf1a49ed24f2a4fb091e6e1e833fccf19935", "search": "01bc42d635e9ea0588741c4c7a2bbd3feb3ac5dc", "search-session": "58a44d14ec991739166b2ec28d718001ab0f4b28", - "search-telemetry": "ab67ef721f294f28d5e10febbd20653347720188", + "search-telemetry": "1bbaf2db531b97fa04399440fa52d46e86d54dd8", "security-rule": "1ff82dfb2298c3caf6888fc3ef15c6bf7a628877", "security-solution-signals-migration": "c2db409c1857d330beb3d6fd188fa186f920302c", "siem-detection-engine-rule-actions": "123c130dc38120a470d8db9fed9a4cebd2046445", @@ -137,7 +137,7 @@ describe('checking migration metadata changes on all registered SO types', () => "slo": "ee0e16abebba5779c37277bc3fe8da1fe1207b7a", "space": "7fc578a1f9f7708cb07479f03953d664ad9f1dae", "spaces-usage-stats": "084bd0f080f94fb5735d7f3cf12f13ec92f36bad", - "synthetics-monitor": "96cc312bfa597022f83dfb3b5d1501e27a73e8d5", + "synthetics-monitor": "7136a2669a65323c56da849f26c369cdeeb3b381", "synthetics-param": "9776c9b571d35f0d0397e8915e035ea1dc026db7", "synthetics-privates-locations": "7d032fc788905e32152029ae7ab3d6038c48ae44", "tag": "87f21f07df9cc37001b15a26e413c18f50d1fbfe", diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/actions/actions.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/actions/actions.test.ts index 6dcb2e881bd54..e51e0ef12a89f 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/actions/actions.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/actions/actions.test.ts @@ -706,9 +706,7 @@ describe('migration actions', () => { // Reindex doesn't return any errors on it's own, so we have to test // together with waitForReindexTask - // - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/154278 - describe.skip('reindex & waitForReindexTask', () => { + describe('reindex & waitForReindexTask', () => { it('resolves right when reindex succeeds without reindex script', async () => { const res = (await reindex({ client, @@ -1076,7 +1074,6 @@ describe('migration actions', () => { } `); }); - it('resolves left wait_for_task_completion_timeout when the task does not finish within the timeout', async () => { await waitForIndexStatus({ client, diff --git a/src/plugins/bfetch/common/buffer/item_buffer.ts b/src/plugins/bfetch/common/buffer/item_buffer.ts index ec3b9ea5747c4..df5960499cc6d 100644 --- a/src/plugins/bfetch/common/buffer/item_buffer.ts +++ b/src/plugins/bfetch/common/buffer/item_buffer.ts @@ -19,7 +19,7 @@ export interface ItemBufferParams { * argument which is a list of all buffered items. If `.flush()` is called * when buffer is empty, `.onflush` is called with empty array. */ - onFlush: (items: Item[]) => void; + onFlush: (items: Item[]) => void | Promise; } /** @@ -60,11 +60,19 @@ export class ItemBuffer { } /** - * Call `.onflush` method and clear buffer. + * Call `.onFlush` method and clear buffer. */ public flush() { + this.flushAsync().catch(() => {}); + } + + /** + * Same as `.flush()` but asynchronous, and returns a promise, which + * rejects if `.onFlush` throws. + */ + public async flushAsync(): Promise { let list; [list, this.list] = [this.list, []]; - this.params.onFlush(list); + await this.params.onFlush(list); } } diff --git a/src/plugins/bfetch/common/buffer/timed_item_buffer.ts b/src/plugins/bfetch/common/buffer/timed_item_buffer.ts index 62be3753a8d58..5c685e387d3fe 100644 --- a/src/plugins/bfetch/common/buffer/timed_item_buffer.ts +++ b/src/plugins/bfetch/common/buffer/timed_item_buffer.ts @@ -41,6 +41,11 @@ export class TimedItemBuffer extends ItemBuffer { super.flush(); } + public async flushAsync() { + clearTimeout(this.timer); + await super.flushAsync(); + } + private onTimeout = () => { this.flush(); }; diff --git a/src/plugins/content_management/README.md b/src/plugins/content_management/README.md index 08bbe41c4f787..94eeb23a50d8f 100644 --- a/src/plugins/content_management/README.md +++ b/src/plugins/content_management/README.md @@ -1,3 +1,22 @@ # Content management The content management plugin provides functionality to manage content in Kibana. + + +## Testing + +Many parts of the Content Management service are implemented *in-memory*, hence it +is possible to test big chunks of the Content Management plugin using Jest +tests. + + +### Elasticsearch Integration tests + +Some functionality of the Content Management plugin can be tested using *Kibana +Integration Tests*, which execute tests against a real Elasticsearch instance. + +Run integrations tests with: + +``` +yarn test:jest_integration src/plugins/content_management +``` diff --git a/src/plugins/content_management/jest.integration.config.js b/src/plugins/content_management/jest.integration.config.js new file mode 100644 index 0000000000000..19a8063326873 --- /dev/null +++ b/src/plugins/content_management/jest.integration.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_integration', + rootDir: '../../..', + roots: ['/src/plugins/content_management'], +}; diff --git a/src/plugins/content_management/server/core/core.test.ts b/src/plugins/content_management/server/core/core.test.ts index 95253d508b6c1..86e3797d2d92b 100644 --- a/src/plugins/content_management/server/core/core.test.ts +++ b/src/plugins/content_management/server/core/core.test.ts @@ -5,6 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import { loggingSystemMock } from '@kbn/core/server/mocks'; import { Core } from './core'; import { createMemoryStorage } from './mocks'; @@ -31,6 +32,8 @@ import type { SearchItemError, } from './event_types'; import { ContentTypeDefinition, StorageContext } from './types'; +import { until } from '../event_stream/tests/util'; +import { setupEventStreamService } from '../event_stream/tests/setup_event_stream_service'; const logger = loggingSystemMock.createLogger(); @@ -48,8 +51,13 @@ const setup = ({ registerFooType = false }: { registerFooType?: boolean } = {}) }, }; - const core = new Core({ logger }); + const eventStream = setupEventStreamService().service; + const core = new Core({ + logger, + eventStream, + }); const coreSetup = core.setup(); + const contentDefinition: ContentTypeDefinition = { id: FOO_CONTENT_ID, storage: createMemoryStorage(), @@ -76,6 +84,7 @@ const setup = ({ registerFooType = false }: { registerFooType?: boolean } = {}) fooContentCrud, cleanUp, eventBus: coreSetup.api.eventBus, + eventStream, }; }; @@ -839,6 +848,41 @@ describe('Content Core', () => { }); }); }); + + describe('eventStream', () => { + test('stores "delete" events', async () => { + const { fooContentCrud, ctx, eventStream } = setup({ registerFooType: true }); + + await fooContentCrud!.create(ctx, { title: 'Hello' }, { id: '1234' }); + await fooContentCrud!.delete(ctx, '1234'); + + const findEvent = async () => { + const tail = await eventStream.tail(); + + for (const event of tail) { + if ( + event.predicate[0] === 'delete' && + event.object && + event.object[0] === 'foo' && + event.object[1] === '1234' + ) { + return event; + } + } + + return null; + }; + + await until(async () => !!(await findEvent()), 100); + + const event = await findEvent(); + + expect(event).toMatchObject({ + predicate: ['delete'], + object: ['foo', '1234'], + }); + }); + }); }); }); }); diff --git a/src/plugins/content_management/server/core/core.ts b/src/plugins/content_management/server/core/core.ts index c8988307ff267..8062e4ca23ea5 100644 --- a/src/plugins/content_management/server/core/core.ts +++ b/src/plugins/content_management/server/core/core.ts @@ -5,8 +5,9 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { Logger } from '@kbn/core/server'; +import { Logger } from '@kbn/core/server'; +import { EventStreamService } from '../event_stream'; import { ContentCrud } from './crud'; import { EventBus } from './event_bus'; import { ContentRegistry } from './registry'; @@ -25,6 +26,11 @@ export interface CoreApi { eventBus: EventBus; } +export interface CoreInitializerContext { + logger: Logger; + eventStream: EventStreamService; +} + export interface CoreSetup { /** Content registry instance */ contentRegistry: ContentRegistry; @@ -36,7 +42,7 @@ export class Core { private contentRegistry: ContentRegistry; private eventBus: EventBus; - constructor({ logger }: { logger: Logger }) { + constructor(private readonly ctx: CoreInitializerContext) { const contentTypeValidator = (contentType: string) => this.contentRegistry?.isContentRegistered(contentType) ?? false; this.eventBus = new EventBus(contentTypeValidator); @@ -44,6 +50,8 @@ export class Core { } setup(): CoreSetup { + this.setupEventStream(); + return { contentRegistry: this.contentRegistry, api: { @@ -53,4 +61,16 @@ export class Core { }, }; } + + private setupEventStream() { + // TODO: This should be cleaned up and support added for all CRUD events. + this.eventBus.on('deleteItemSuccess', (event) => { + this.ctx.eventStream.addEvent({ + // TODO: add "subject" field to event + predicate: ['delete'], + // TODO: the `.contentId` should be easily available on most events. + object: [event.contentTypeId, (event as any).contentId], + }); + }); + } } diff --git a/src/plugins/content_management/server/event_stream/README.md b/src/plugins/content_management/server/event_stream/README.md new file mode 100644 index 0000000000000..95eef47060503 --- /dev/null +++ b/src/plugins/content_management/server/event_stream/README.md @@ -0,0 +1,50 @@ +# Event Stream + + +## The service + +On a high-level the Event Stream is exposed through the `EventStreamService` +class, which is the public interface to the Event Stream, it holds any necessary +state, and follows plugin life-cycle methods. + +The service also validates the events before they are stored. It also buffers +the events on write. Events are buffered for 250ms or up to 100 events before +they are flushed to the storage. + + +## The client + +On a lower level the actual event storage is defined in the `EventStreamClient` +interface. There are two `EventStreamClient` implementations: + +- `EsEventStreamClient` is the production implementation, which stores events + to the Elasticsearch. +- `MemoryEventStreamClient` is used for testing and could be used for demo + purposes. + + +### The `EsEventStreamClient` client + +`EsEventStreamClient` is used in production. It stores events in the +`.kibana-event-stream` data stream. The data stream and index template are +created during plugin initialization "start" life-cycle. + +The mappings define `meta` and `indexed` fields, which are reserved for future +schema extensions (so that new fields can be added without mapping changes). + +The mappings also define a transaction ID (`txID`) field, which can be used to +correlate multiple related events together or to store the transaction ID. + +Events are written to Elasticsearch using the `_bulk` request API. + + +## Testing + +The `MemoryEventStreamClient` can be used to simulate the Event Stream in Jest +unit test environment. Use `setupEventStreamService()` to spin up the service +in the test environment. + +The clients themselves can be tested using the `testEventStreamClient` test +suite, which should help with verifying that both implements work correctly. +The `EsEventStreamClient` it is tested using Kibana integration tests, but for +`MemoryEventStreamClient` it is tested as a Jest tests. diff --git a/src/plugins/content_management/server/event_stream/es/es_event_stream_client.ts b/src/plugins/content_management/server/event_stream/es/es_event_stream_client.ts new file mode 100644 index 0000000000000..749c6b2ad19ef --- /dev/null +++ b/src/plugins/content_management/server/event_stream/es/es_event_stream_client.ts @@ -0,0 +1,189 @@ +/* + * Copyright 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 { estypes } from '@elastic/elasticsearch'; +import { KueryNode, nodeBuilder, toElasticsearchQuery } from '@kbn/es-query'; +import type { EsClient, EsEventStreamEventDto } from './types'; +import type { + EventStreamClient, + EventStreamClientFilterOptions, + EventStreamClientFilterResult, + EventStreamEvent, + EventStreamLogger, +} from '../types'; +import { EsEventStreamNames } from './es_event_stream_names'; +import { EsEventStreamInitializer } from './init/es_event_stream_initializer'; +import { eventToDto, dtoToEvent } from './util'; + +export interface EsEventStreamClientDependencies { + baseName: string; + kibanaVersion: string; + logger: EventStreamLogger; + esClient: Promise; +} + +const sort: estypes.Sort = [ + { + // By default we always sort by event timestamp descending. + '@timestamp': { + order: 'desc', + }, + + // Tie breakers for events with the same timestamp. + subjectId: { + order: 'desc', + }, + objectId: { + order: 'desc', + }, + predicate: { + order: 'desc', + }, + }, +]; + +export class EsEventStreamClient implements EventStreamClient { + readonly #names: EsEventStreamNames; + + constructor(private readonly deps: EsEventStreamClientDependencies) { + this.#names = new EsEventStreamNames(deps.baseName); + } + + public async initialize(): Promise { + const initializer = new EsEventStreamInitializer({ + names: this.#names, + kibanaVersion: this.deps.kibanaVersion, + logger: this.deps.logger, + esClient: this.deps.esClient, + }); + await initializer.initialize(); + } + + public async writeEvents(events: EventStreamEvent[]): Promise { + if (events.length === 0) return; + + const esClient = await this.deps.esClient; + const operations: Array = []; + + for (const event of events) { + const dto = eventToDto(event); + + operations.push({ create: {} }, dto); + } + + const { errors } = await esClient.bulk( + { + index: this.#names.dataStream, + operations, + }, + { + maxRetries: 0, + } + ); + + if (errors) { + throw new Error('Some events failed to be indexed.'); + } + } + + public async tail(limit: number = 100): Promise { + return (await this.filter({ limit })).events; + } + + public async filter( + options: EventStreamClientFilterOptions + ): Promise { + const esClient = await this.deps.esClient; + const topLevelNodes: KueryNode[] = []; + + if (options.subject && options.subject.length) { + topLevelNodes.push( + nodeBuilder.or( + options.subject.map(([type, id]) => + !id + ? nodeBuilder.is('subjectType', type) + : nodeBuilder.and([ + nodeBuilder.is('subjectType', type), + nodeBuilder.is('subjectId', id), + ]) + ) + ) + ); + } + + if (options.object && options.object.length) { + topLevelNodes.push( + nodeBuilder.or( + options.object.map(([type, id]) => + !id + ? nodeBuilder.is('objectType', type) + : nodeBuilder.and([ + nodeBuilder.is('objectType', type), + nodeBuilder.is('objectId', id), + ]) + ) + ) + ); + } + + if (options.predicate && options.predicate.length) { + topLevelNodes.push( + nodeBuilder.or(options.predicate.map((type) => nodeBuilder.is('predicate', type))) + ); + } + + if (options.transaction && options.transaction.length) { + topLevelNodes.push( + nodeBuilder.or(options.transaction.map((id) => nodeBuilder.is('txId', id))) + ); + } + + if (options.from) { + const from = new Date(options.from).toISOString(); + const node = nodeBuilder.range('@timestamp', 'gte', from); + + topLevelNodes.push(node); + } + + if (options.to) { + const to = new Date(options.to).toISOString(); + const node = nodeBuilder.range('@timestamp', 'lte', to); + + topLevelNodes.push(node); + } + + const query = toElasticsearchQuery(nodeBuilder.and(topLevelNodes)); + const size = options.limit ?? 100; + const request: estypes.SearchRequest = { + index: this.#names.dataStream, + query, + sort, + size, + track_total_hits: false, + }; + + if (options.cursor) { + request.search_after = JSON.parse(options.cursor); + } + + const res = await esClient.search(request); + const events = res.hits.hits.map((hit) => dtoToEvent(hit._source!)); + const lastHit = res.hits.hits[res.hits.hits.length - 1]; + + let cursor: string = ''; + + if (events.length >= size && lastHit && lastHit.sort) { + cursor = JSON.stringify(lastHit.sort); + } + + return { + cursor, + events, + }; + } +} diff --git a/src/plugins/content_management/server/event_stream/es/es_event_stream_client_factory.ts b/src/plugins/content_management/server/event_stream/es/es_event_stream_client_factory.ts new file mode 100644 index 0000000000000..d43e1a8eee152 --- /dev/null +++ b/src/plugins/content_management/server/event_stream/es/es_event_stream_client_factory.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { CoreSetup } from '@kbn/core/server'; +import type { EventStreamClient, EventStreamClientFactory, EventStreamLogger } from '../types'; +import { EsEventStreamClient } from './es_event_stream_client'; + +export interface EsEventStreamClientFactoryDependencies { + /** + * The prefix used for index names. Usually `.kibana`, as Elasticsearch + * treats indices starting with the `.kibana*` prefix as a special indices + * that only Kibana should be allowed to access. + */ + baseName: string; + kibanaVersion: string; + logger: EventStreamLogger; +} + +export class EsEventStreamClientFactory implements EventStreamClientFactory { + constructor(private readonly deps: EsEventStreamClientFactoryDependencies) {} + + public create(core: CoreSetup): EventStreamClient { + const startServices = core.getStartServices(); + + return new EsEventStreamClient({ + ...this.deps, + esClient: startServices.then(([{ elasticsearch }]) => elasticsearch.client.asInternalUser), + }); + } +} diff --git a/src/plugins/content_management/server/event_stream/es/es_event_stream_names.ts b/src/plugins/content_management/server/event_stream/es/es_event_stream_names.ts new file mode 100644 index 0000000000000..1ede04fd21515 --- /dev/null +++ b/src/plugins/content_management/server/event_stream/es/es_event_stream_names.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export class EsEventStreamNames { + public readonly base: string; + public readonly dataStream: string; + public readonly indexPattern: string; + public readonly indexTemplate: string; + + constructor(baseName: string) { + const EVENT_STREAM_SUFFIX = `-event-stream`; + const dataStream = `${baseName}${EVENT_STREAM_SUFFIX}`; + + this.base = baseName; + this.dataStream = dataStream; + this.indexPattern = `${dataStream}*`; + this.indexTemplate = `${dataStream}-template`; + } +} diff --git a/src/plugins/content_management/server/event_stream/es/index.ts b/src/plugins/content_management/server/event_stream/es/index.ts new file mode 100644 index 0000000000000..6caddfdce93bb --- /dev/null +++ b/src/plugins/content_management/server/event_stream/es/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { + EsEventStreamClient, + type EsEventStreamClientDependencies, +} from './es_event_stream_client'; +export { + EsEventStreamClientFactory, + type EsEventStreamClientFactoryDependencies, +} from './es_event_stream_client_factory'; diff --git a/src/plugins/content_management/server/event_stream/es/init/es_event_stream_initializer.ts b/src/plugins/content_management/server/event_stream/es/init/es_event_stream_initializer.ts new file mode 100644 index 0000000000000..9cd5d69d07191 --- /dev/null +++ b/src/plugins/content_management/server/event_stream/es/init/es_event_stream_initializer.ts @@ -0,0 +1,127 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 pRetry from 'p-retry'; +import { errors } from '@elastic/elasticsearch'; +import type { EsClient } from '../types'; +import type { EsEventStreamNames } from '../es_event_stream_names'; +import type { EventStreamLogger } from '../../types'; +import { newIndexTemplateRequest } from './index_template'; + +export interface EsEventStreamInitializerDependencies { + names: EsEventStreamNames; + kibanaVersion: string; + logger: EventStreamLogger; + esClient: Promise; +} + +export class EsEventStreamInitializer { + constructor(private readonly deps: EsEventStreamInitializerDependencies) {} + + public async initialize(): Promise { + const createdIndexTemplate = await this.#retry( + this.createIndexTemplateIfNotExists, + 'createIndexTemplateIfNotExists' + ); + + if (createdIndexTemplate) { + await this.#retry(this.createDataStream, 'createDataStream'); + } + } + + /** + * Calls a function; retries calling it multiple times via p-retry, if it fails. + * Should retry on 2s, 4s, 8s, 16s. + * + * See: https://github.com/tim-kos/node-retry#retryoperationoptions + * + * @param fn Function to retry, if it fails. + */ + readonly #retry = async (fn: () => Promise, fnName: string): Promise => { + this.deps.logger.debug(`Event Stream initialization operation: ${fnName}`); + + return await pRetry(fn, { + minTimeout: 1000, + maxTimeout: 1000 * 60 * 3, + retries: 4, + factor: 2, + randomize: true, + onFailedAttempt: (err) => { + const message = + `Event Stream initialization operation failed and will be retried: ${fnName};` + + `${err.retriesLeft} more times; error: ${err.message}`; + + this.deps.logger.warn(message); + }, + }); + }; + + protected readonly createIndexTemplateIfNotExists = async (): Promise => { + const exists = await this.indexTemplateExists(); + if (exists) return false; + return await this.createIndexTemplate(); + }; + + protected async indexTemplateExists(): Promise { + try { + const esClient = await this.deps.esClient; + const name = this.deps.names.indexTemplate; + const exists = await esClient.indices.existsIndexTemplate({ name }); + + return !!exists; + } catch (err) { + throw new Error(`error checking existence of index template: ${err.message}`); + } + } + + protected async createIndexTemplate(): Promise { + try { + const esClient = await this.deps.esClient; + const { indexTemplate, indexPattern } = this.deps.names; + const request = newIndexTemplateRequest({ + name: indexTemplate, + indexPatterns: [indexPattern], + kibanaVersion: this.deps.kibanaVersion, + }); + + await esClient.indices.putIndexTemplate(request); + + return true; + } catch (err) { + // The error message doesn't have a type attribute we can look to guarantee it's due + // to the template already existing (only long message) so we'll check ourselves to see + // if the template now exists. This scenario would happen if you startup multiple Kibana + // instances at the same time. + const exists = await this.indexTemplateExists(); + + if (exists) return false; + + const error = new Error(`error creating index template: ${err.message}`); + Object.assign(error, { wrapped: err }); + throw error; + } + } + + protected readonly createDataStream = async (): Promise => { + const esClient = await this.deps.esClient; + const name = this.deps.names.dataStream; + + try { + await esClient.indices.createDataStream({ + name, + }); + } catch (error) { + const alreadyExists = + (error as errors.ResponseError)?.body?.error?.type === 'resource_already_exists_exception'; + + if (alreadyExists) return; + + throw error; + } + }; +} diff --git a/src/plugins/content_management/server/event_stream/es/init/index_template.ts b/src/plugins/content_management/server/event_stream/es/init/index_template.ts new file mode 100644 index 0000000000000..40a0535eebd44 --- /dev/null +++ b/src/plugins/content_management/server/event_stream/es/init/index_template.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { estypes } from '@elastic/elasticsearch'; +import { mappings } from './mappings'; + +export interface NewIndexTemplateRequestParams { + name: string; + indexPatterns: string[]; + kibanaVersion: string; +} + +export const newIndexTemplateRequest = ( + params: NewIndexTemplateRequestParams +): estypes.IndicesPutIndexTemplateRequest => { + const version = 1; + const { name, indexPatterns, kibanaVersion } = params; + + return { + name, + // This will create the template only if it doesn't exist. + create: true, + // This object is required to make it a data stream template. + data_stream: { + hidden: true, + }, + // Our own metadata to keep track of the template. + _meta: { + description: 'This data stream stores events for the Kibana content_management plugin.', + // Template version. + version, + // Kibana version when the template was created. + kibanaVersion, + }, + // Setting this to something higher than the default 0 will allow + // to define lower priority templates in the future. + priority: 50, + version, + index_patterns: indexPatterns, + template: { + settings: { + number_of_shards: 1, + auto_expand_replicas: '0-1', + 'index.hidden': true, + }, + mappings, + }, + }; +}; diff --git a/src/plugins/content_management/server/event_stream/es/init/mappings.ts b/src/plugins/content_management/server/event_stream/es/init/mappings.ts new file mode 100644 index 0000000000000..e63b826e0dc40 --- /dev/null +++ b/src/plugins/content_management/server/event_stream/es/init/mappings.ts @@ -0,0 +1,88 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { estypes } from '@elastic/elasticsearch'; + +export const mappings: estypes.MappingTypeMapping = { + dynamic: false, + properties: { + /** + * Every document indexed to a data stream must contain a `@timestamp` + * field, mapped as a `date` or `date_nanos` field type. + */ + '@timestamp': { + type: 'date', + }, + + /** Subject is the content item who/which performed the event. */ + subjectType: { + type: 'keyword', + ignore_above: 256, + }, + subjectId: { + type: 'keyword', + ignore_above: 256, + }, + + /** Object is the content item on which the event was performed. */ + objectType: { + type: 'keyword', + ignore_above: 256, + }, + objectId: { + type: 'keyword', + ignore_above: 256, + }, + + /** The event type. */ + predicate: { + type: 'keyword', + ignore_above: 256, + }, + + /** Custom payload, may be be different per event type. */ + payload: { + type: 'object', + enabled: false, + dynamic: false, + }, + + /** + * Transaction ID which allows to trace the event back to the original + * request or to correlate multiple events. For example, one user action + * can result in multiple events, all of which will have the same `txId`. + */ + txId: { + type: 'keyword', + ignore_above: 256, + }, + + /** + * Reserved for future extensions. Event Stream client can add custom + * private fields here in the future if needed, without having to update + * the index template mappings. + */ + meta: { + type: 'object', + enabled: false, + dynamic: false, + }, + + /** + * Reserved for the future extensions, same as the `meta` field, but fields + * added to this object will be indexed. + * + * See dynamic field mapping rules: https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-field-mapping.html + */ + indexed: { + type: 'object', + enabled: true, + dynamic: true, + }, + }, +}; diff --git a/src/plugins/content_management/server/event_stream/es/integration_tests/es_event_stream_client.test.ts b/src/plugins/content_management/server/event_stream/es/integration_tests/es_event_stream_client.test.ts new file mode 100644 index 0000000000000..35a16260684e3 --- /dev/null +++ b/src/plugins/content_management/server/event_stream/es/integration_tests/es_event_stream_client.test.ts @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { ElasticsearchClient } from '@kbn/core/server'; +import { + createTestServers, + type TestElasticsearchUtils, + type TestKibanaUtils, +} from '@kbn/core-test-helpers-kbn-server'; +import { EsEventStreamClient } from '../es_event_stream_client'; +import { EsEventStreamNames } from '../es_event_stream_names'; +import { EventStreamLoggerMock } from '../../tests/event_stream_logger_mock'; +import { testEventStreamClient } from '../../tests/test_event_stream_client'; + +describe('EsEventStreamClient', () => { + let manageES: TestElasticsearchUtils; + let manageKbn: TestKibanaUtils; + let esClient: ElasticsearchClient; + let resolveClient: (client: EsEventStreamClient) => void = () => {}; + const client: Promise = new Promise((resolve) => { + resolveClient = resolve; + }); + + const baseName = '.kibana-test'; + const names = new EsEventStreamNames(baseName); + + beforeAll(async () => { + const { startES, startKibana } = createTestServers({ adjustTimeout: jest.setTimeout }); + + manageES = await startES(); + manageKbn = await startKibana(); + esClient = manageKbn.coreStart.elasticsearch.client.asInternalUser; + resolveClient( + new EsEventStreamClient({ + baseName, + esClient: Promise.resolve(esClient), + kibanaVersion: '1.2.3', + logger: new EventStreamLoggerMock(), + }) + ); + }); + + afterAll(async () => { + await manageKbn.root.shutdown(); + await manageKbn.stop(); + await manageES.stop(); + }); + + it('can initialize the Event Stream', async () => { + const exists1 = await esClient.indices.existsIndexTemplate({ + name: names.indexTemplate, + }); + + expect(exists1).toBe(false); + + await (await client).initialize(); + + const exists2 = await esClient.indices.existsIndexTemplate({ + name: names.indexTemplate, + }); + + expect(exists2).toBe(true); + }); + + it('should return "resource_already_exists_exception" error when data stream already exists', async () => { + try { + await esClient.indices.createDataStream({ + name: names.dataStream, + }); + throw new Error('Not expected'); + } catch (error) { + expect(error.body.error.type).toBe('resource_already_exists_exception'); + } + }); + + testEventStreamClient(client); +}); diff --git a/src/plugins/content_management/server/event_stream/es/types.ts b/src/plugins/content_management/server/event_stream/es/types.ts new file mode 100644 index 0000000000000..e370f97c5ba71 --- /dev/null +++ b/src/plugins/content_management/server/event_stream/es/types.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { Client } from '@elastic/elasticsearch'; + +export type EsClient = Omit< + Client, + 'connectionPool' | 'serializer' | 'extend' | 'close' | 'diagnostic' +>; + +/** + * Represents a single event as it is stored in Elasticsearch. + */ +export interface EsEventStreamEventDto { + /** + * Time when the event occurred. + */ + '@timestamp': string; + + /** + * Type of the subject. Subject is the content item who/which performed the + * event. + */ + subjectType?: string; + + /** + * ID of the subject. + */ + subjectId?: string; + + /** + * Type of the object. Object is the content item on which the event was + * performed. + */ + objectType?: string; + + /** + * ID of the object. + */ + objectId?: string; + + /** + * Specifies the event type. Such as `create`, `update`, `delete`, etc. + */ + predicate: string; + + /** + * Custom payload, maybe be different per event type. Provided by the + * event type originator. + */ + payload?: Record; + + /** + * Transaction ID which allows to trace the event back to the original + * request or to correlate multiple events. For example, one user action + * can result in multiple events, all of which will have the same `txId`. + */ + txId?: string; + + /** + * Reserved for future extensions. Custom metadata may be added here by the + * Event Stream implementation. + */ + meta?: Record; + + /** + * Reserved for future extensions. Same as `meta`, but indexed. + */ + indexed?: Record; +} diff --git a/src/plugins/content_management/server/event_stream/es/util.ts b/src/plugins/content_management/server/event_stream/es/util.ts new file mode 100644 index 0000000000000..b447a581bb53f --- /dev/null +++ b/src/plugins/content_management/server/event_stream/es/util.ts @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { Mutable } from 'utility-types'; +import type { EventStreamEvent } from '../types'; +import type { EsEventStreamEventDto } from './types'; + +export const eventToDto = (event: EventStreamEvent): EsEventStreamEventDto => { + const { time, subject, predicate, object, transaction } = event; + + const dto: EsEventStreamEventDto = { + '@timestamp': new Date(time).toISOString(), + predicate: predicate[0], + }; + + if (subject) { + dto.subjectType = subject[0]; + dto.subjectId = subject[1]; + } + + if (predicate[1]) { + dto.payload = predicate[1]; + } + + if (object) { + dto.objectType = object[0]; + dto.objectId = object[1]; + } + + if (transaction) { + dto.txId = transaction; + } + + return dto; +}; + +export const dtoToEvent = (dto: EsEventStreamEventDto): EventStreamEvent => { + const { + '@timestamp': timestamp, + subjectType, + subjectId, + predicate, + payload, + objectId, + objectType, + txId, + } = dto; + + const event: Mutable = { + time: new Date(timestamp).getTime(), + predicate: payload ? [predicate, payload] : [predicate], + }; + + if (subjectType && subjectId) { + event.subject = [subjectType, subjectId]; + } + + if (objectType && objectId) { + event.object = [objectType, objectId]; + } + + if (txId) { + event.transaction = txId; + } + + return event; +}; diff --git a/src/plugins/content_management/server/event_stream/event_stream_service.test.ts b/src/plugins/content_management/server/event_stream/event_stream_service.test.ts new file mode 100644 index 0000000000000..59d00c3a69856 --- /dev/null +++ b/src/plugins/content_management/server/event_stream/event_stream_service.test.ts @@ -0,0 +1,333 @@ +/* + * Copyright 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 { setupEventStreamService } from './tests/setup_event_stream_service'; + +const setup = setupEventStreamService; + +describe('EventStreamService', () => { + describe('.tail()', () => { + test('returns no events by default', async () => { + const { service } = setup(); + + service.flush(); + + const events = await service.tail(); + + expect(events).toStrictEqual([]); + }); + }); + + describe('validation', () => { + describe('event time', () => { + test('cannot be too far in the future', async () => { + const { service } = setup(); + expect(() => + service.addEvents([ + { + time: 4000000000000, + predicate: ['test'], + }, + ]) + ).toThrow(); + }); + + test('cannot be too far in the past', async () => { + const { service } = setup(); + expect(() => + service.addEvents([ + { + time: 1, + predicate: ['test'], + }, + ]) + ).toThrow(); + }); + + test('cannot be a float', async () => { + const { service } = setup(); + expect(() => + service.addEvents([ + { + time: Date.now() + 0.5, + predicate: ['test'], + }, + ]) + ).toThrow(); + }); + + test('cannot be a string', async () => { + const { service } = setup(); + expect(() => + service.addEvents([ + { + time: String(Date.now()) as any, + predicate: ['test'], + }, + ]) + ).toThrow(); + }); + }); + + describe('event subject', () => { + test('type cannot be empty', async () => { + const { service } = setup(); + expect(() => + service.addEvents([ + { + subject: ['', '123'], + predicate: ['test'], + }, + ]) + ).toThrow(); + }); + + test('type cannot be too long', async () => { + const { service } = setup(); + expect(() => + service.addEvents([ + { + subject: [ + '0123456789012345678901234567890123456789012345678901234567890123456789', + '123', + ], + predicate: ['test'], + }, + ]) + ).toThrow(); + }); + + test('ID cannot be too long', async () => { + const { service } = setup(); + expect(() => + service.addEvents([ + { + subject: [ + 'dashboard', + '012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789', + ], + predicate: ['test'], + }, + ]) + ).toThrow(); + }); + + test('cannot be null', async () => { + const { service } = setup(); + expect(() => + service.addEvents([ + { + subject: null as any, + predicate: ['test'], + }, + ]) + ).toThrow(); + }); + }); + + describe('event predicate', () => { + test('type cannot be missing', async () => { + const { service } = setup(); + expect(() => service.addEvents([{} as any])).toThrow(); + }); + + test('type cannot be null', async () => { + const { service } = setup(); + expect(() => + service.addEvents([ + { + predicate: null as any, + }, + ]) + ).toThrow(); + }); + + test('type cannot be a number', async () => { + const { service } = setup(); + expect(() => + service.addEvents([ + { + predicate: [123 as any], + }, + ]) + ).toThrow(); + }); + + test('type cannot be an empty string', async () => { + const { service } = setup(); + expect(() => + service.addEvents([ + { + predicate: [''], + }, + ]) + ).toThrow(); + }); + + test('cannot be a long string', async () => { + const { service } = setup(); + expect(() => + service.addEvents([ + { + predicate: ['0123456789012345678901234567890123456789012345678901234567890123456789'], + }, + ]) + ).toThrow(); + }); + + test('can be a short string', async () => { + const { service } = setup(); + service.addEvents([ + { + predicate: ['view'], + }, + ]); + }); + + test('can have attributes', async () => { + const { service } = setup(); + service.addEvents([ + { + predicate: [ + 'view', + { + foo: 'bar', + }, + ], + }, + ]); + }); + + test('attributes cannot be empty', async () => { + const { service } = setup(); + expect(() => + service.addEvents([ + { + predicate: ['view', {}], + }, + ]) + ).toThrow(); + }); + + test('attributes cannot be null', async () => { + const { service } = setup(); + expect(() => + service.addEvents([ + { + predicate: ['view', null as any], + }, + ]) + ).toThrow(); + }); + }); + + describe('event object', () => { + test('type cannot be empty', async () => { + const { service } = setup(); + expect(() => + service.addEvents([ + { + object: ['', '123'], + predicate: ['test'], + }, + ]) + ).toThrow(); + }); + + test('type cannot be too long', async () => { + const { service } = setup(); + expect(() => + service.addEvents([ + { + object: [ + '0123456789012345678901234567890123456789012345678901234567890123456789', + '123', + ], + predicate: ['test'], + }, + ]) + ).toThrow(); + }); + + test('ID cannot be too long', async () => { + const { service } = setup(); + expect(() => + service.addEvents([ + { + object: [ + 'dashboard', + '012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789', + ], + predicate: ['test'], + }, + ]) + ).toThrow(); + }); + + test('cannot be null', async () => { + const { service } = setup(); + expect(() => + service.addEvents([ + { + object: null as any, + predicate: ['test'], + }, + ]) + ).toThrow(); + }); + }); + + describe('event transaction', () => { + test('can be missing', async () => { + const { service } = setup(); + service.addEvents([ + { + predicate: ['test'], + }, + ]); + }); + + test('cannot be empty', async () => { + const { service } = setup(); + expect(() => + service.addEvents([ + { + predicate: ['test'], + transaction: '', + }, + ]) + ).toThrow(); + }); + + test('cannot be too long', async () => { + const { service } = setup(); + expect(() => + service.addEvents([ + { + predicate: ['test'], + transaction: + '012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789', + }, + ]) + ).toThrow(); + }); + + test('cannot be an integer', async () => { + const { service } = setup(); + expect(() => + service.addEvents([ + { + predicate: ['test'], + transaction: 123 as any, + }, + ]) + ).toThrow(); + }); + }); + }); +}); diff --git a/src/plugins/content_management/server/event_stream/event_stream_service.ts b/src/plugins/content_management/server/event_stream/event_stream_service.ts new file mode 100644 index 0000000000000..d5be158c95bb0 --- /dev/null +++ b/src/plugins/content_management/server/event_stream/event_stream_service.ts @@ -0,0 +1,168 @@ +/* + * Copyright 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 { CoreSetup } from '@kbn/core/server'; +import { TimedItemBuffer } from '@kbn/bfetch-plugin/common'; +import type { + EventStreamClient, + EventStreamClientFactory, + EventStreamClientFilterOptions, + EventStreamClientFilterResult, + EventStreamEvent, + EventStreamEventPartial, + EventStreamLogger, +} from './types'; +import { partialEventValidator } from './validation'; + +export interface EventStreamInitializerContext { + logger: EventStreamLogger; + clientFactory: EventStreamClientFactory; +} + +export interface EventStreamSetup { + core: CoreSetup; +} + +export class EventStreamService { + protected client?: EventStreamClient; + readonly #buffer: TimedItemBuffer; + + constructor(private readonly ctx: EventStreamInitializerContext) { + this.#buffer = new TimedItemBuffer({ + flushOnMaxItems: 100, + maxItemAge: 250, + onFlush: async (events: EventStreamEvent[]) => { + const { logger } = this.ctx; + + if (!this.client) { + logger.error('EventStreamClient is not initialized, events will not be written.'); + return; + } + + try { + await this.client.writeEvents(events); + } catch (error) { + logger.error('Failed to write events to Event Stream.'); + logger.error(error); + } + }, + }); + } + + /** Called during "setup" plugin life-cycle. */ + public setup({ core }: EventStreamSetup): void { + this.client = this.ctx.clientFactory.create(core); + } + + /** Called during "start" plugin life-cycle. */ + public start(): void { + const { logger } = this.ctx; + + if (!this.client) throw new Error('EventStreamClient not initialized.'); + + logger.debug('Initializing Event Stream.'); + this.client + .initialize() + .then(() => { + logger.debug('Event Stream was initialized.'); + }) + .catch((error) => { + logger.error('Failed to initialize Event Stream. Events will not be indexed.'); + logger.error(error); + }); + } + + /** Called during "stop" plugin life-cycle. */ + public async stop(): Promise { + await this.#buffer.flushAsync(); + } + + #getClient(): EventStreamClient { + if (!this.client) throw new Error('EventStreamClient not initialized.'); + return this.client; + } + + /** + * Validates a single event. Throws an error if the event is invalid. + * + * @param event A partial event to validate. + */ + protected validatePartialEvent(event: EventStreamEventPartial): void { + partialEventValidator(event); + if (partialEventValidator.errors) { + const error = partialEventValidator.errors[0]; + if (!error) throw new Error('Validation failed.'); + throw new Error(`Validation error at [path = ${error.instancePath}]: ${error.message}`); + } + } + + /** + * Queues an event to be written to the Event Stream. The event is appended to + * a buffer and written to the Event Stream periodically. + * + * Events are flushed once the buffer reaches 100 items or 250ms has passed, + * whichever comes first. To force a flush, call `.flush()`. + * + * @param event Event to add to the Event Stream. + */ + public addEvent(event: EventStreamEventPartial): void { + this.validatePartialEvent(event); + + const completeEvent: EventStreamEvent = { + ...event, + time: event.time || Date.now(), + }; + + this.#buffer.write(completeEvent); + } + + /** + * Same as `.addEvent()` but accepts an array of events. + * + * @param events Events to add to the Event Stream. + */ + public addEvents(events: EventStreamEventPartial[]): void { + for (const event of events) { + this.addEvent(event); + } + } + + /** + * Flushes the event buffer, writing all events to the Event Stream. + */ + public flush(): void { + this.#buffer.flush(); + } + + /** + * Read latest events from the Event Stream. + * + * @param limit Number of events to return. Defaults to 100. + * @returns Latest events from the Event Stream. + */ + public async tail(limit: number = 100): Promise { + const client = this.#getClient(); + + return await client.tail(limit); + } + + /** + * Retrieves events from the Event Stream which match the specified filter + * options. + * + * @param options Filtering options. + * @returns Paginated results of events matching the filter. + */ + public async filter( + options: EventStreamClientFilterOptions + ): Promise { + const client = this.#getClient(); + + return await client.filter(options); + } +} diff --git a/src/plugins/content_management/server/event_stream/index.ts b/src/plugins/content_management/server/event_stream/index.ts new file mode 100644 index 0000000000000..479f30fe2eeda --- /dev/null +++ b/src/plugins/content_management/server/event_stream/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './types'; +export * from './es'; +export { EventStreamService } from './event_stream_service'; diff --git a/src/plugins/content_management/server/event_stream/memory/index.ts b/src/plugins/content_management/server/event_stream/memory/index.ts new file mode 100644 index 0000000000000..7cef77935e74c --- /dev/null +++ b/src/plugins/content_management/server/event_stream/memory/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { MemoryEventStreamClient } from './memory_event_stream_client'; +export { MemoryEventStreamClientFactory } from './memory_event_stream_client_factory'; diff --git a/src/plugins/content_management/server/event_stream/memory/memory_event_stream_client.test.ts b/src/plugins/content_management/server/event_stream/memory/memory_event_stream_client.test.ts new file mode 100644 index 0000000000000..5239139ac7cd9 --- /dev/null +++ b/src/plugins/content_management/server/event_stream/memory/memory_event_stream_client.test.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { MemoryEventStreamClient } from './memory_event_stream_client'; +import { testEventStreamClient } from '../tests/test_event_stream_client'; + +const client = new MemoryEventStreamClient(); + +describe('MemoryEventStreamClient', () => { + testEventStreamClient(Promise.resolve(client)); +}); diff --git a/src/plugins/content_management/server/event_stream/memory/memory_event_stream_client.ts b/src/plugins/content_management/server/event_stream/memory/memory_event_stream_client.ts new file mode 100644 index 0000000000000..ef5ba3352bd4a --- /dev/null +++ b/src/plugins/content_management/server/event_stream/memory/memory_event_stream_client.ts @@ -0,0 +1,123 @@ +/* + * Copyright 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 { + EventStreamClient, + EventStreamClientFilterOptions, + EventStreamClientFilterResult, + EventStreamEvent, +} from '../types'; +import { clone } from './util'; + +/** + * This is an in-memory implementation of the {@link EventStreamClient} + * interface (it does not persist events to Elasticsearch). It is useful for + * testing and demo purposes. + */ +export class MemoryEventStreamClient implements EventStreamClient { + #events: EventStreamEvent[] = []; + + public async initialize(): Promise {} + + public async writeEvents(events: EventStreamEvent[]): Promise { + for (const event of events) { + this.#events.push(clone(event)); + } + this.#events.sort((a, b) => b.time - a.time); + } + + public async tail(limit: number = 100): Promise { + const tail = this.#events.slice(0, limit); + + return tail.map(clone); + } + + public async filter( + options: EventStreamClientFilterOptions + ): Promise { + let events: EventStreamEvent[] = [...this.#events]; + + const { subject, object, predicate, transaction, from, to } = options; + + if (subject && subject.length) { + events = events.filter((event) => { + if (!event.subject) { + return false; + } + + return subject.some(([type, id]) => { + if (!id) return type === event.subject![0]; + return type === event.subject![0] && id === event.subject![1]; + }); + }); + } + + if (object && object.length) { + events = events.filter((event) => { + if (!event.object) { + return false; + } + + return object.some(([type, id]) => { + if (!id) return type === event.object![0]; + return type === event.object![0] && id === event.object![1]; + }); + }); + } + + if (predicate && predicate.length) { + events = events.filter((event) => { + if (!event.predicate) { + return false; + } + + return predicate.some((type) => { + if (type && type !== event.predicate![0]) { + return false; + } + + return true; + }); + }); + } + + if (transaction && transaction.length) { + events = events.filter((event) => { + return !event.transaction ? false : transaction.some((id) => event.transaction === id); + }); + } + + if (from) { + events = events.filter((event) => event.time >= from); + } + + if (to) { + events = events.filter((event) => event.time <= to); + } + + const size = options.limit ?? 100; + const offset = options.cursor ? JSON.parse(options.cursor) : 0; + + events = events.slice(offset); + + if (events.length > size) { + events = events.slice(0, size); + } + + let cursor: string = ''; + + if (events.length >= size) { + cursor = JSON.stringify(offset + size); + } + + return { + cursor, + events: events.map(clone), + }; + } +} diff --git a/src/plugins/content_management/server/event_stream/memory/memory_event_stream_client_factory.ts b/src/plugins/content_management/server/event_stream/memory/memory_event_stream_client_factory.ts new file mode 100644 index 0000000000000..7cf4ff8f44cf6 --- /dev/null +++ b/src/plugins/content_management/server/event_stream/memory/memory_event_stream_client_factory.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { EventStreamClient, EventStreamClientFactory } from '../types'; +import { MemoryEventStreamClient } from './memory_event_stream_client'; + +export class MemoryEventStreamClientFactory implements EventStreamClientFactory { + public create(): EventStreamClient { + return new MemoryEventStreamClient(); + } +} diff --git a/src/plugins/content_management/server/event_stream/memory/util.ts b/src/plugins/content_management/server/event_stream/memory/util.ts new file mode 100644 index 0000000000000..94fe3bf69b627 --- /dev/null +++ b/src/plugins/content_management/server/event_stream/memory/util.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const clone = (x: unknown) => JSON.parse(JSON.stringify(x)); diff --git a/src/plugins/content_management/server/event_stream/tests/event_stream_logger_mock.ts b/src/plugins/content_management/server/event_stream/tests/event_stream_logger_mock.ts new file mode 100644 index 0000000000000..2adc4eadb6201 --- /dev/null +++ b/src/plugins/content_management/server/event_stream/tests/event_stream_logger_mock.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { EventStreamLogger } from '../types'; + +export class EventStreamLoggerMock implements EventStreamLogger { + public debug = jest.fn(); + public info = jest.fn(); + public warn = jest.fn(); + public error = jest.fn(); +} diff --git a/src/plugins/content_management/server/event_stream/tests/setup_event_stream_service.ts b/src/plugins/content_management/server/event_stream/tests/setup_event_stream_service.ts new file mode 100644 index 0000000000000..3fcb01224607e --- /dev/null +++ b/src/plugins/content_management/server/event_stream/tests/setup_event_stream_service.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 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 CoreSetup } from '@kbn/core/server'; +import { coreMock } from '@kbn/core/server/mocks'; +import { EventStreamService } from '../event_stream_service'; +import { EventStreamLoggerMock } from './event_stream_logger_mock'; +import { MemoryEventStreamClientFactory } from '../memory'; + +export const setupEventStreamService = (kibanaCoreSetup: CoreSetup = coreMock.createSetup()) => { + const logger = new EventStreamLoggerMock(); + const clientFactory = new MemoryEventStreamClientFactory(); + const service = new EventStreamService({ + logger, + clientFactory, + }); + + service.setup({ core: kibanaCoreSetup }); + service.start(); + + return { + logger, + clientFactory, + service, + }; +}; diff --git a/src/plugins/content_management/server/event_stream/tests/test_event_stream_client.ts b/src/plugins/content_management/server/event_stream/tests/test_event_stream_client.ts new file mode 100644 index 0000000000000..d01fe9bdeff4b --- /dev/null +++ b/src/plugins/content_management/server/event_stream/tests/test_event_stream_client.ts @@ -0,0 +1,410 @@ +/* + * Copyright 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 { until } from './util'; +import { EventStreamClient, EventStreamEvent } from '../types'; + +export const testEventStreamClient = (clientPromise: Promise) => { + let now = Date.now(); + const getTime = () => now++; + const items: EventStreamEvent[] = [ + { + predicate: ['test', { foo: 'bar' }], + time: getTime(), + }, + { + time: getTime(), + subject: ['user', '1'], + predicate: ['create', { foo: 'bar' }], + object: ['dashboard', '1'], + }, + { + time: getTime(), + subject: ['user', '2'], + predicate: ['view'], + object: ['map', 'xyz'], + }, + { + time: getTime(), + subject: ['user', '2'], + predicate: ['view'], + object: ['canvas', 'xxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'], + }, + { + time: getTime(), + subject: ['user', '55'], + predicate: [ + 'share', + { + foo: 'bar', + baz: 'qux', + nested: { + value: 123, + }, + }, + ], + object: ['dashboard', '1'], + }, + { + time: getTime(), + subject: ['user', '1'], + predicate: ['view'], + object: ['map', 'xyz'], + }, + { + time: getTime(), + subject: ['user', '2'], + predicate: ['view'], + object: ['canvas', 'yyyy-yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy'], + }, + ]; + + describe('.writeEvents()', () => { + it('can write a single event', async () => { + const client = await clientPromise; + + await client.writeEvents([items[0]]); + + await until(async () => { + const events = await client.tail(); + return events.length === 1; + }, 100); + + const tail = await client.tail(); + + expect(tail).toMatchObject([items[0]]); + }); + + it('can write multiple events', async () => { + const client = await clientPromise; + const events: EventStreamEvent[] = [items[1], items[2], items[3]]; + + await client.writeEvents(events); + + await until(async () => { + const tail = await client.tail(); + return tail.length === 4; + }, 100); + + const tail = await client.tail(); + + expect(tail.slice(0, 3)).toMatchObject([events[2], events[1], events[0]]); + }); + }); + + describe('.tail()', () => { + it('can limit events to last 2', async () => { + const client = await clientPromise; + const events: EventStreamEvent[] = [items[4], items[5], items[6]]; + + await client.writeEvents(events); + + await until(async () => { + const tail = await client.tail(); + return tail.length === 7; + }, 100); + + const tail = await client.tail(2); + + expect(tail.length).toBe(2); + expect(tail).toMatchObject([events[2], events[1]]); + }); + }); + + describe('.filter()', () => { + it('can fetch all events, cursor is empty', async () => { + const result = await (await clientPromise).filter({}); + + // console.log(JSON.stringify(result, null, 2)); + + expect(result.cursor).toBe(''); + expect(result.events.length).toBe(7); + expect(result.events).toMatchObject(items.slice(0, 7).sort((a, b) => b.time - a.time)); + }); + + it('can paginate through results', async () => { + const client = await clientPromise; + const result1 = await client.filter({ limit: 3, cursor: '' }); + const result2 = await client.filter({ limit: 3, cursor: result1.cursor }); + const result3 = await client.filter({ limit: 3, cursor: result2.cursor }); + + expect(!!result1.cursor).toBe(true); + expect(!!result2.cursor).toBe(true); + expect(!!result3.cursor).toBe(false); + + expect(result1.events.length).toBe(3); + expect(result2.events.length).toBe(3); + expect(result3.events.length).toBe(1); + }); + + it('can select all results but one, and then the last result', async () => { + const client = await clientPromise; + const result1 = await client.filter({ limit: 6, cursor: '' }); + const result2 = await client.filter({ limit: 106, cursor: result1.cursor }); + + expect(!!result1.cursor).toBe(true); + expect(!!result2.cursor).toBe(false); + expect(result1.events.length).toBe(6); + expect(result2.events.length).toBe(1); + expect(result2.events).toMatchObject([items[0]]); + }); + + it('can limit starting time range of results', async () => { + const client = await clientPromise; + const result = await client.filter({ + from: items[2].time, + }); + + expect(result.cursor).toBe(''); + expect(result.events.length).toBe(5); + expect(result.events).toMatchObject(items.slice(2, 7).sort((a, b) => b.time - a.time)); + }); + + it('can limit ending time range of results', async () => { + const client = await clientPromise; + const result = await client.filter({ + to: items[2].time, + }); + + expect(result.cursor).toBe(''); + expect(result.events.length).toBe(3); + expect(result.events).toMatchObject(items.slice(0, 3).sort((a, b) => b.time - a.time)); + }); + + it('can limit starting and ending time ranges of results', async () => { + const client = await clientPromise; + const result = await client.filter({ + from: items[3].time, + to: items[5].time, + }); + + expect(result.cursor).toBe(''); + expect(result.events.length).toBe(3); + expect(result.events).toMatchObject(items.slice(3, 6).sort((a, b) => b.time - a.time)); + }); + + it('can filter results for a single subject', async () => { + const client = await clientPromise; + const result = await client.filter({ + subject: [['user', '55']], + }); + + expect(result.cursor).toBe(''); + expect(result.events.length).toBe(1); + expect(result.events[0]).toStrictEqual({ + time: expect.any(Number), + subject: ['user', '55'], + predicate: [ + 'share', + { + foo: 'bar', + baz: 'qux', + nested: { + value: 123, + }, + }, + ], + object: ['dashboard', '1'], + }); + }); + + it('can filter results for multiple subjects', async () => { + const client = await clientPromise; + const result = await client.filter({ + subject: [ + ['user', '55'], + ['user', '1'], + ], + }); + + expect(result.cursor).toBe(''); + expect(result.events.length).toBe(3); + expect(result.events).toMatchObject([items[5], items[4], items[1]]); + }); + + it('can filter results for a single object', async () => { + const client = await clientPromise; + const event = items[6]; + const result = await client.filter({ + object: [event.object!], + }); + + expect(result.cursor).toBe(''); + expect(result.events.length).toBe(1); + expect(result.events[0]).toStrictEqual(event); + }); + + it('can filter results for a two objects', async () => { + const client = await clientPromise; + const result = await client.filter({ + object: [ + ['dashboard', '1'], + ['map', 'xyz'], + ], + }); + + expect(result.cursor).toBe(''); + expect(result.events.length).toBe(4); + }); + + it('can filter results by predicate type', async () => { + const client = await clientPromise; + const result = await client.filter({ + predicate: ['view'], + }); + + expect(result.cursor).toBe(''); + expect(result.events.length).toBe(4); + expect(result.events).toMatchObject([items[6], items[5], items[3], items[2]]); + }); + + it('can filter results by multiple predicates', async () => { + const client = await clientPromise; + const result = await client.filter({ + predicate: ['create', 'share'], + }); + + expect(result.cursor).toBe(''); + expect(result.events.length).toBe(2); + expect(result.events).toMatchObject([items[4], items[1]]); + }); + + it('can combine multiple filters using AND clause', async () => { + const client = await clientPromise; + const result = await client.filter({ + subject: [['user', '1']], + object: [['dashboard', '1']], + from: items[0].time, + to: items[6].time, + }); + + expect(result.cursor).toBe(''); + expect(result.events.length).toBe(1); + expect(result.events[0]).toMatchObject(items[1]); + }); + }); + + describe('transactions', () => { + it('can associate multiple events with one transaction', async () => { + const client = await clientPromise; + const time = getTime(); + const transaction = 'my-transaction-id'; + const events: EventStreamEvent[] = [ + { + transaction, + time, + subject: ['user', '123'], + predicate: [ + 'tag', + { + use: ['foo', 'bar'], + disuse: ['baz'], + }, + ], + object: ['visualization', '666'], + }, + { + transaction, + time, + subject: ['visualization', '666'], + predicate: ['use'], + object: ['tag', 'foo'], + }, + { + transaction, + time, + subject: ['visualization', '666'], + predicate: ['use'], + object: ['tag', 'bar'], + }, + { + transaction, + time, + subject: ['visualization', '666'], + predicate: ['disuse'], + object: ['tag', 'baz'], + }, + ]; + + await client.writeEvents(events); + + const getEvents = async () => { + return await client.filter({ + transaction: [transaction], + }); + }; + + await until(async () => { + const result = await getEvents(); + return result.events.length === 4; + }, 100); + + const result = await getEvents(); + + expect(result.events.length).toBe(4); + expect( + result.events.some( + (event) => event.object![0] === 'visualization' && event.object![1] === '666' + ) + ).toBe(true); + expect( + result.events.some((event) => event.object![0] === 'tag' && event.object![1] === 'foo') + ).toBe(true); + expect( + result.events.some((event) => event.object![0] === 'tag' && event.object![1] === 'bar') + ).toBe(true); + expect( + result.events.some((event) => event.object![0] === 'tag' && event.object![1] === 'baz') + ).toBe(true); + }); + + it('can filter out two transactions', async () => { + const client = await clientPromise; + const events: EventStreamEvent[] = [ + { + transaction: 'tx-1', + time: getTime(), + subject: ['user', '123'], + predicate: ['do-something'], + object: ['map', '101'], + }, + { + transaction: 'tx-1', + time: getTime(), + subject: ['user', '123'], + predicate: ['do-something'], + object: ['canvas', '202'], + }, + { + transaction: 'tx-2', + time: getTime(), + subject: ['api-key', 'abc'], + predicate: ['do-something-else'], + object: ['map', '101'], + }, + ]; + + await client.writeEvents(events); + + const getEvents = async () => { + return await client.filter({ + transaction: ['tx-1', 'tx-2'], + }); + }; + + await until(async () => { + const result = await getEvents(); + return result.events.length === 3; + }, 100); + + const result = await getEvents(); + + expect(result.events.length).toBe(3); + }); + }); +}; diff --git a/src/plugins/content_management/server/event_stream/tests/util.ts b/src/plugins/content_management/server/event_stream/tests/util.ts new file mode 100644 index 0000000000000..91d7606f7d500 --- /dev/null +++ b/src/plugins/content_management/server/event_stream/tests/util.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const tick = (ms: number = 1) => new Promise((r) => setTimeout(r, ms)); + +export const until = async (check: () => boolean | Promise, pollInterval: number = 1) => { + do { + if (await check()) return; + await tick(pollInterval); + } while (true); +}; diff --git a/src/plugins/content_management/server/event_stream/types.ts b/src/plugins/content_management/server/event_stream/types.ts new file mode 100644 index 0000000000000..0438fa27bc906 --- /dev/null +++ b/src/plugins/content_management/server/event_stream/types.ts @@ -0,0 +1,181 @@ +/* + * Copyright 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 { CoreSetup } from '@kbn/core/server'; +import type { Optional } from 'utility-types'; + +/** + * Represents a factory which can be used to create an Event Stream client. + */ +export interface EventStreamClientFactory { + /** + * Creates an Event Stream client. + * + * @param core The CoreSetup object provided by the Kibana platform. + */ + create(core: CoreSetup): EventStreamClient; +} + +/** + * Represents a storage layer for events. + */ +export interface EventStreamClient { + /** + * Initializes the Event Stream client. This method is run at the plugin's + * `setup` phase. It should be used to create any necessary resources. + */ + initialize(): Promise; + + /** + * Immediately writes one or more events to the Event Stream using a bulk + * request. + * + * @param events One or more events to write to the Event Stream. + */ + writeEvents: (events: EventStreamEvent[]) => Promise; + + /** + * Retrieves the most recent events from the Event Stream. + * + * @param limit The maximum number of events to return. If not specified, the + * default is 100. + */ + tail(limit?: number): Promise; + + /** + * Retrieves events from the Event Stream which match the specified filter + * options. + */ + filter: (options: EventStreamClientFilterOptions) => Promise; +} + +/** + * Represents the options which can be used to filter events from the Event + * Stream. Top level properties are joined by `AND` logic. For example, if + * `subject` and `predicate` are specified, only events which match both + * criteria will be returned. + */ +export interface EventStreamClientFilterOptions { + /** + * One or more subjects to filter by. Subjects are joined by `OR` logic. + */ + readonly subject?: Array; + + /** + * One or more predicates to filter by. Predicates are joined by `OR` logic. + */ + readonly predicate?: string[]; + + /** + * One or more objects to filter by. Objects are joined by `OR` logic. + */ + readonly object?: Array; + + /** + * One or more transaction IDs to filter by. Transactions are joined by `OR` + * logic. + */ + readonly transaction?: string[]; + + /** + * The starting time to filter by. Events which occurred after this time will + * be returned. If not specified, the default is the beginning of time. + */ + readonly from?: number; + + /** + * The ending time to filter by. Events which occurred before this time will + * be returned. If not specified, the default is the current time. + */ + readonly to?: number; + + /** + * The maximum number of events to return. If not specified, the default is + * 100. + */ + readonly limit?: number; + + /** + * A cursor which can be used to retrieve the next page of results. On the + * first call, this should be `undefined` or empty string. On subsequent + * calls, this should be the value of the `cursor` property returned by the + * previous call in the {@link EventStreamClientFilterResult} object. + */ + readonly cursor?: string; +} + +/** + * Represents the result of a `.filter()` operation. + */ +export interface EventStreamClientFilterResult { + /** + * A cursor which can be used to retrieve the next page of results. Should be + * treated as a opaque value. When empty, there are no more results. + */ + readonly cursor: string; + + /** + * The list of events which matched the filter. Sorted by time in descending + * order. + */ + readonly events: EventStreamEvent[]; +} + +/** + * Represents a single event in the Event Stream. Events can be thought of as + * "Semantic triples" (see https://en.wikipedia.org/wiki/Semantic_triple). + * Semantic triples have a subject, a predicate, and an object. In the context + * of the Event Stream, the subject is the content item who/which performed the + * event, the predicate is the event type (such as `create`, `update`, `delete`, + * etc.), and the object is the content item on which the action was performed. + */ +export interface EventStreamEvent { + /** + * Specifies who performed the event. The subject is a tuple of the type of + * the subject and the ID of the subject. + */ + readonly subject?: readonly [type: string, id: string]; + + /** + * Specifies the event type. Such as `create`, `update`, `delete`, etc. + * The predicate is a tuple of the type of the predicate and any attributes + * associated with the predicate. + */ + readonly predicate: readonly [type: string, attributes?: Record]; + + /** + * Specifies the content item on which the event was performed. The object is + * a tuple of the type of the object and the ID of the object. + */ + readonly object?: readonly [type: string, id: string]; + + /** + * Timestamp in milliseconds since the Unix Epoch when the event occurred. + */ + readonly time: number; + + /** + * Transaction ID, which allows to trace the event back to the original + * request. As well as to associate multiple events together. + */ + readonly transaction?: string; +} + +/** + * Represents a partial version of an EventStreamEvent, as it can be provided + * for ingestion. The `time` property is optional, as it will be set by the + * Event Stream if not provided. + */ +export type EventStreamEventPartial = Optional; + +import type { Logger } from '@kbn/core/server'; + +/** + * Logger interface used in the Event Stream. + */ +export type EventStreamLogger = Pick; diff --git a/src/plugins/content_management/server/event_stream/validation.ts b/src/plugins/content_management/server/event_stream/validation.ts new file mode 100644 index 0000000000000..19bc43061042d --- /dev/null +++ b/src/plugins/content_management/server/event_stream/validation.ts @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 Ajv from 'ajv/dist/2020'; + +const ajv = new Ajv({ + allErrors: false, + strict: false, + verbose: false, +}); + +const partialEventSchema = { + type: 'object', + required: ['predicate'], + additionalProperties: false, + properties: { + subject: { + type: 'array', + nullable: false, + minItems: 2, + maxItems: 2, + items: false, + prefixItems: [ + { + type: 'string', + minLength: 1, + maxLength: 64, + }, + { + type: 'string', + maxLength: 255, + }, + ], + }, + predicate: { + type: 'array', + minItems: 1, + maxItems: 2, + items: false, + nullable: false, + prefixItems: [ + { + type: 'string', + minLength: 1, + maxLength: 64, + }, + { + type: 'object', + additionalProperties: true, + minProperties: 1, + maxProperties: 255, + }, + ], + }, + object: { + type: 'array', + nullable: false, + minItems: 2, + maxItems: 2, + items: false, + prefixItems: [ + { + type: 'string', + minLength: 1, + maxLength: 64, + }, + { + type: 'string', + maxLength: 255, + }, + ], + }, + time: { + type: 'number', + nullable: false, + multipleOf: 1, + // Just some sane limits so the number doesn't escape too far into the + // future or past. + minimum: 1600000000000, // Sep 2020 + maximum: 2600000000000, // May 2052 + }, + transaction: { + type: 'string', + nullable: false, + minLength: 1, + maxLength: 255, + }, + }, +}; + +export const partialEventValidator = ajv.compile(partialEventSchema); diff --git a/src/plugins/content_management/server/plugin.test.ts b/src/plugins/content_management/server/plugin.test.ts index f67652da6d938..fbf0e632d2552 100644 --- a/src/plugins/content_management/server/plugin.test.ts +++ b/src/plugins/content_management/server/plugin.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { loggingSystemMock, coreMock } from '@kbn/core/server/mocks'; +import { coreMock } from '@kbn/core/server/mocks'; import { ContentManagementPlugin } from './plugin'; import { IRouter } from '@kbn/core/server'; import type { ProcedureName } from '../common'; @@ -62,22 +62,29 @@ jest.mock('./rpc/procedures/all_procedures', () => { }); const setup = () => { - const logger = loggingSystemMock.create(); - const { http } = coreMock.createSetup(); + const coreSetup = coreMock.createSetup(); + const router: IRouter = coreSetup.http.createRouter(); + const http = { ...coreSetup.http, createRouter: () => router }; + const plugin = new ContentManagementPlugin(coreMock.createPluginInitializerContext()); - const router: IRouter = http.createRouter(); router.post = jest.fn(); - const plugin = new ContentManagementPlugin({ logger }); - - return { plugin, http: { createRouter: () => router }, router }; + return { + plugin, + http, + router, + coreSetup: { + ...coreSetup, + http, + }, + }; }; describe('ContentManagementPlugin', () => { describe('setup()', () => { test('should expose the core API', () => { - const { plugin, http } = setup(); - const api = plugin.setup({ http }); + const { plugin, coreSetup } = setup(); + const api = plugin.setup(coreSetup); expect(Object.keys(api).sort()).toEqual(['crud', 'eventBus', 'register']); expect(api.crud('')).toBe('mockedCrud'); @@ -87,8 +94,8 @@ describe('ContentManagementPlugin', () => { describe('RPC', () => { test('should create a single POST HTTP route on the router', () => { - const { plugin, http, router } = setup(); - plugin.setup({ http }); + const { plugin, coreSetup, router } = setup(); + plugin.setup(coreSetup); expect(router.post).toBeCalledTimes(1); const [routeConfig]: Parameters = (router.post as jest.Mock).mock.calls[0]; @@ -97,8 +104,8 @@ describe('ContentManagementPlugin', () => { }); test('should register all the procedures in the RPC service and the route handler must send to each procedure the core request context + the request body as input', async () => { - const { plugin, http, router } = setup(); - plugin.setup({ http }); + const { plugin, coreSetup, router } = setup(); + plugin.setup(coreSetup); const [_, handler]: Parameters = (router.post as jest.Mock).mock.calls[0]; @@ -139,8 +146,8 @@ describe('ContentManagementPlugin', () => { }); test('should return error in custom error format', async () => { - const { plugin, http, router } = setup(); - plugin.setup({ http }); + const { plugin, coreSetup, router } = setup(); + plugin.setup(coreSetup); const [_, handler]: Parameters = (router.post as jest.Mock).mock.calls[0]; diff --git a/src/plugins/content_management/server/plugin.ts b/src/plugins/content_management/server/plugin.ts index 56685c0564973..e70bb5da14a65 100755 --- a/src/plugins/content_management/server/plugin.ts +++ b/src/plugins/content_management/server/plugin.ts @@ -21,22 +21,37 @@ import { ContentManagementServerStart, SetupDependencies, } from './types'; +import { EventStreamService, EsEventStreamClientFactory } from './event_stream'; import { procedureNames } from '../common/rpc'; -type CreateRouterFn = CoreSetup['http']['createRouter']; - export class ContentManagementPlugin implements Plugin { private readonly logger: Logger; private readonly core: Core; + readonly #eventStream: EventStreamService; + + constructor(initializerContext: PluginInitializerContext) { + const kibanaVersion = initializerContext.env.packageInfo.version; - constructor(initializerContext: { logger: PluginInitializerContext['logger'] }) { this.logger = initializerContext.logger.get(); - this.core = new Core({ logger: this.logger }); + this.#eventStream = new EventStreamService({ + logger: this.logger, + clientFactory: new EsEventStreamClientFactory({ + baseName: '.kibana', + kibanaVersion, + logger: this.logger, + }), + }); + this.core = new Core({ + logger: this.logger, + eventStream: this.#eventStream, + }); } - public setup(core: { http: { createRouter: CreateRouterFn } }) { + public setup(core: CoreSetup) { + this.#eventStream.setup({ core }); + const { api: coreApi, contentRegistry } = this.core.setup(); const rpc = new RpcService(); @@ -54,6 +69,16 @@ export class ContentManagementPlugin } public start(core: CoreStart) { + this.#eventStream.start(); + return {}; } + + public async stop(): Promise { + try { + await this.#eventStream.stop(); + } catch (e) { + this.logger.error(`Error during event stream stop: ${e}`); + } + } } diff --git a/src/plugins/content_management/tsconfig.json b/src/plugins/content_management/tsconfig.json index 692aa3a03176e..ead8bd9af5a06 100644 --- a/src/plugins/content_management/tsconfig.json +++ b/src/plugins/content_management/tsconfig.json @@ -8,6 +8,9 @@ "@kbn/core", "@kbn/config-schema", "@kbn/core-http-request-handler-context-server", + "@kbn/es-query", + "@kbn/core-test-helpers-kbn-server", + "@kbn/bfetch-plugin", "@kbn/object-versioning", ], "exclude": [ diff --git a/src/plugins/controls/public/control_group/editor/control_editor.tsx b/src/plugins/controls/public/control_group/editor/control_editor.tsx index 61f0c574d3f01..5cc94acaebcf1 100644 --- a/src/plugins/controls/public/control_group/editor/control_editor.tsx +++ b/src/plugins/controls/public/control_group/editor/control_editor.tsx @@ -16,6 +16,7 @@ import React, { useEffect, useState } from 'react'; import useMount from 'react-use/lib/useMount'; +import useAsync from 'react-use/lib/useAsync'; import { EuiFlyoutHeader, @@ -35,7 +36,7 @@ import { EuiSwitch, EuiTextColor, } from '@elastic/eui'; -import { DataViewListItem, DataView, DataViewField } from '@kbn/data-views-plugin/common'; +import { DataViewField } from '@kbn/data-views-plugin/common'; import { LazyDataViewPicker, LazyFieldPicker, @@ -46,7 +47,6 @@ import { ControlGroupStrings } from '../control_group_strings'; import { ControlEmbeddable, ControlWidth, - DataControlFieldRegistry, DataControlInput, IEditableControlFactory, } from '../../types'; @@ -71,12 +71,6 @@ interface EditControlProps { onTypeEditorChange: (partial: Partial) => void; } -interface ControlEditorState { - dataViewListItems: DataViewListItem[]; - selectedDataView?: DataView; - selectedField?: DataViewField; -} - const FieldPicker = withSuspense(LazyFieldPicker, null); const DataViewPicker = withSuspense(LazyDataViewPicker, null); @@ -104,10 +98,6 @@ export const ControlEditor = ({ const { useEmbeddableSelector: select } = useControlGroupContainerContext(); const editorConfig = select((state) => state.componentState.editorConfig); - const [state, setState] = useState({ - dataViewListItems: [], - }); - const [defaultTitle, setDefaultTitle] = useState(); const [currentTitle, setCurrentTitle] = useState(title ?? ''); const [currentWidth, setCurrentWidth] = useState(width); @@ -116,47 +106,54 @@ export const ControlEditor = ({ const [selectedField, setSelectedField] = useState( embeddable ? embeddable.getInput().fieldName : undefined ); - - const [fieldRegistry, setFieldRegistry] = useState(); - useEffect(() => { - (async () => { - if (state.selectedDataView?.id) { - setFieldRegistry(await getDataControlFieldRegistry(await get(state.selectedDataView.id))); - } - })(); - }, [state.selectedDataView?.id, get]); + const [selectedDataViewId, setSelectedDataViewId] = useState(); useMount(() => { let mounted = true; if (selectedField) setDefaultTitle(selectedField); (async () => { - const dataViewListItems = await getIdsWithTitle(); + if (!mounted) return; + const initialId = embeddable?.getInput().dataViewId ?? getRelevantDataViewId?.() ?? (await getDefaultId()); - let dataView: DataView | undefined; if (initialId) { onTypeEditorChange({ dataViewId: initialId }); - dataView = await get(initialId); + setSelectedDataViewId(initialId); } - if (!mounted) return; - setState((s) => ({ - ...s, - selectedDataView: dataView, - dataViewListItems, - })); })(); return () => { mounted = false; }; }); + const { loading: dataViewListLoading, value: dataViewListItems = [] } = useAsync(() => { + return getIdsWithTitle(); + }); + + const { + loading: dataViewLoading, + value: { selectedDataView, fieldRegistry } = { + selectedDataView: undefined, + fieldRegistry: undefined, + }, + } = useAsync(async () => { + if (!selectedDataViewId) { + return; + } + const dataView = await get(selectedDataViewId); + const registry = await getDataControlFieldRegistry(dataView); + return { + selectedDataView: dataView, + fieldRegistry: registry, + }; + }, [selectedDataViewId]); + useEffect( - () => setControlEditorValid(Boolean(selectedField) && Boolean(state.selectedDataView)), - [selectedField, setControlEditorValid, state.selectedDataView] + () => setControlEditorValid(Boolean(selectedField) && Boolean(selectedDataView)), + [selectedField, setControlEditorValid, selectedDataView] ); - const { selectedDataView: dataView } = state; const controlType = selectedField && fieldRegistry && fieldRegistry[selectedField].compatibleControlTypes[0]; const factory = controlType && getControlFactory(controlType); @@ -178,47 +175,44 @@ export const ControlEditor = ({ {!editorConfig?.hideDataViewSelector && ( { setLastUsedDataViewId?.(dataViewId); - if (dataViewId === dataView?.id) return; - + if (dataViewId === selectedDataViewId) return; onTypeEditorChange({ dataViewId }); setSelectedField(undefined); - get(dataViewId).then((newDataView) => { - setState((s) => ({ ...s, selectedDataView: newDataView })); - }); + setSelectedDataViewId(dataViewId); }} trigger={{ label: - state.selectedDataView?.getName() ?? + selectedDataView?.getName() ?? ControlGroupStrings.manageControl.getSelectDataViewMessage(), }} + selectableProps={{ isLoading: dataViewListLoading }} /> )} - {fieldRegistry && ( - - Boolean(fieldRegistry[field.name])} - selectedFieldName={selectedField} - dataView={dataView} - onSelectField={(field) => { - onTypeEditorChange({ - fieldName: field.name, - }); - const newDefaultTitle = field.displayName ?? field.name; - setDefaultTitle(newDefaultTitle); - setSelectedField(field.name); - if (!currentTitle || currentTitle === defaultTitle) { - setCurrentTitle(newDefaultTitle); - updateTitle(newDefaultTitle); - } - }} - /> - - )} + + Boolean(fieldRegistry?.[field.name])} + selectedFieldName={selectedField} + dataView={selectedDataView} + onSelectField={(field) => { + onTypeEditorChange({ + fieldName: field.name, + }); + const newDefaultTitle = field.displayName ?? field.name; + setDefaultTitle(newDefaultTitle); + setSelectedField(field.name); + if (!currentTitle || currentTitle === defaultTitle) { + setCurrentTitle(newDefaultTitle); + updateTitle(newDefaultTitle); + } + }} + selectableProps={{ isLoading: dataViewListLoading || dataViewLoading }} + /> + {factory ? ( @@ -284,7 +278,7 @@ export const ControlEditor = ({ )} diff --git a/src/plugins/controls/public/control_group/editor/data_control_editor_tools.ts b/src/plugins/controls/public/control_group/editor/data_control_editor_tools.ts index 8cfd3cebe5dac..19ca230c0f354 100644 --- a/src/plugins/controls/public/control_group/editor/data_control_editor_tools.ts +++ b/src/plugins/controls/public/control_group/editor/data_control_editor_tools.ts @@ -11,7 +11,7 @@ import { memoize } from 'lodash'; import { DataView } from '@kbn/data-views-plugin/common'; import { pluginServices } from '../../services'; -import { DataControlField, DataControlFieldRegistry, IEditableControlFactory } from '../../types'; +import { DataControlFieldRegistry, IEditableControlFactory } from '../../types'; export const getDataControlFieldRegistry = memoize( async (dataView: DataView) => { @@ -30,20 +30,19 @@ const loadFieldRegistryFromDataView = async ( const controlFactories = getControlTypes().map( (controlType) => getControlFactory(controlType) as IEditableControlFactory ); - const fieldRegistry: DataControlFieldRegistry = dataView.fields - .getAll() - .reduce((registry, field) => { - const test: DataControlField = { field, compatibleControlTypes: [] }; + const fieldRegistry: DataControlFieldRegistry = {}; + return new Promise((resolve) => { + for (const field of dataView.fields.getAll()) { + const compatibleControlTypes = []; for (const factory of controlFactories) { - if (factory.isFieldCompatible) { - factory.isFieldCompatible(test); + if (factory.isFieldCompatible && factory.isFieldCompatible(field)) { + compatibleControlTypes.push(factory.type); } } - if (test.compatibleControlTypes.length === 0) { - return { ...registry }; + if (compatibleControlTypes.length > 0) { + fieldRegistry[field.name] = { field, compatibleControlTypes }; } - return { ...registry, [field.name]: test }; - }, {}); - - return fieldRegistry; + } + resolve(fieldRegistry); + }); }; diff --git a/src/plugins/controls/public/options_list/embeddable/options_list_embeddable_factory.tsx b/src/plugins/controls/public/options_list/embeddable/options_list_embeddable_factory.tsx index 8465016ce4402..d970c309bb6a8 100644 --- a/src/plugins/controls/public/options_list/embeddable/options_list_embeddable_factory.tsx +++ b/src/plugins/controls/public/options_list/embeddable/options_list_embeddable_factory.tsx @@ -9,6 +9,7 @@ import deepEqual from 'fast-deep-equal'; import { i18n } from '@kbn/i18n'; +import { DataViewField } from '@kbn/data-views-plugin/common'; import { lazyLoadReduxEmbeddablePackage } from '@kbn/presentation-util-plugin/public'; import { EmbeddableFactoryDefinition, IContainer } from '@kbn/embeddable-plugin/public'; @@ -21,7 +22,7 @@ import { OPTIONS_LIST_CONTROL, } from '../../../common/options_list/types'; import { OptionsListEditorOptions } from '../components/options_list_editor_options'; -import { ControlEmbeddable, DataControlField, IEditableControlFactory } from '../../types'; +import { ControlEmbeddable, IEditableControlFactory } from '../../types'; export class OptionsListEmbeddableFactory implements EmbeddableFactoryDefinition, IEditableControlFactory @@ -57,15 +58,13 @@ export class OptionsListEmbeddableFactory return newInput; }; - public isFieldCompatible = (dataControlField: DataControlField) => { - if ( - !dataControlField.field.spec.scripted && - ((dataControlField.field.aggregatable && dataControlField.field.type === 'string') || - dataControlField.field.type === 'boolean' || - dataControlField.field.type === 'ip') - ) { - dataControlField.compatibleControlTypes.push(this.type); - } + public isFieldCompatible = (field: DataViewField) => { + return ( + !field.spec.scripted && + ((field.aggregatable && field.type === 'string') || + field.type === 'boolean' || + field.type === 'ip') + ); }; public controlEditorOptionsComponent = OptionsListEditorOptions; diff --git a/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx b/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx index 2a86d5c186fc2..b8cd0d9536b1e 100644 --- a/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx +++ b/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx @@ -10,6 +10,7 @@ import deepEqual from 'fast-deep-equal'; import { EmbeddableFactoryDefinition, IContainer } from '@kbn/embeddable-plugin/public'; import { lazyLoadReduxEmbeddablePackage } from '@kbn/presentation-util-plugin/public'; +import { DataViewField } from '@kbn/data-views-plugin/common'; import { i18n } from '@kbn/i18n'; import { @@ -20,7 +21,7 @@ import { RangeSliderEmbeddableInput, RANGE_SLIDER_CONTROL, } from '../../../common/range_slider/types'; -import { ControlEmbeddable, DataControlField, IEditableControlFactory } from '../../types'; +import { ControlEmbeddable, IEditableControlFactory } from '../../types'; export class RangeSliderEmbeddableFactory implements EmbeddableFactoryDefinition, IEditableControlFactory @@ -68,10 +69,8 @@ export class RangeSliderEmbeddableFactory return newInput; }; - public isFieldCompatible = (dataControlField: DataControlField) => { - if (dataControlField.field.aggregatable && dataControlField.field.type === 'number') { - dataControlField.compatibleControlTypes.push(this.type); - } + public isFieldCompatible = (field: DataViewField) => { + return field.aggregatable && field.type === 'number'; }; public inject = createRangeSliderInject(); diff --git a/src/plugins/controls/public/types.ts b/src/plugins/controls/public/types.ts index 6e26440c1410d..bdae4d983078c 100644 --- a/src/plugins/controls/public/types.ts +++ b/src/plugins/controls/public/types.ts @@ -49,13 +49,14 @@ export type ControlEmbeddable< /** * Control embeddable editor types */ -export interface IEditableControlFactory { +export interface IEditableControlFactory + extends Pick { controlEditorOptionsComponent?: (props: ControlEditorProps) => JSX.Element; presaveTransformFunction?: ( newState: Partial, embeddable?: ControlEmbeddable ) => Partial; - isFieldCompatible?: (dataControlField: DataControlField) => void; // reducer + isFieldCompatible?: (field: DataViewField) => boolean; } export interface ControlEditorProps { diff --git a/src/plugins/data/server/saved_objects/kql_telemetry.ts b/src/plugins/data/server/saved_objects/kql_telemetry.ts index 4c887e93fac65..51575ad2d3d54 100644 --- a/src/plugins/data/server/saved_objects/kql_telemetry.ts +++ b/src/plugins/data/server/saved_objects/kql_telemetry.ts @@ -7,19 +7,17 @@ */ import { SavedObjectsType } from '@kbn/core/server'; +import { SCHEMA_KQL_TELEMETRY_V8_8_0 } from './schemas/kql_telemetry'; export const kqlTelemetry: SavedObjectsType = { name: 'kql-telemetry', namespaceType: 'agnostic', hidden: false, mappings: { - properties: { - optInCount: { - type: 'long', - }, - optOutCount: { - type: 'long', - }, - }, + dynamic: false, + properties: {}, + }, + schemas: { + '8.8.0': SCHEMA_KQL_TELEMETRY_V8_8_0, }, }; diff --git a/src/plugins/data/server/saved_objects/schemas/kql_telemetry.ts b/src/plugins/data/server/saved_objects/schemas/kql_telemetry.ts new file mode 100644 index 0000000000000..46b787c8915f7 --- /dev/null +++ b/src/plugins/data/server/saved_objects/schemas/kql_telemetry.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 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 { schema } from '@kbn/config-schema'; + +export const SCHEMA_KQL_TELEMETRY_V8_8_0 = schema.object({ + optInCount: schema.maybe(schema.number()), + optOutCount: schema.maybe(schema.number()), +}); diff --git a/src/plugins/data/server/saved_objects/schemas/search_telemetry.ts b/src/plugins/data/server/saved_objects/schemas/search_telemetry.ts new file mode 100644 index 0000000000000..c0af21a063bd8 --- /dev/null +++ b/src/plugins/data/server/saved_objects/schemas/search_telemetry.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { schema } from '@kbn/config-schema'; + +// As per `CollectedUsage` +export const SCHEMA_SEARCH_TELEMETRY_V8_8_0 = schema.object({ + successCount: schema.maybe(schema.number()), + errorCount: schema.maybe(schema.number()), + totalDuration: schema.maybe(schema.number()), +}); diff --git a/src/plugins/data/server/saved_objects/search_telemetry.ts b/src/plugins/data/server/saved_objects/search_telemetry.ts index 9878db7f81cc0..2f72712801648 100644 --- a/src/plugins/data/server/saved_objects/search_telemetry.ts +++ b/src/plugins/data/server/saved_objects/search_telemetry.ts @@ -8,6 +8,7 @@ import { SavedObjectsType } from '@kbn/core/server'; import { migrate712 } from './migrations/to_v7_12_0'; +import { SCHEMA_SEARCH_TELEMETRY_V8_8_0 } from './schemas/search_telemetry'; export const searchTelemetry: SavedObjectsType = { name: 'search-telemetry', @@ -20,4 +21,7 @@ export const searchTelemetry: SavedObjectsType = { migrations: { '7.12.0': migrate712, }, + schemas: { + '8.8.0': SCHEMA_SEARCH_TELEMETRY_V8_8_0, + }, }; diff --git a/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.tsx index 04a7aed0ff2de..8a7592a92050d 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.tsx @@ -14,6 +14,7 @@ import { useDiscoverHistogram } from './use_discover_histogram'; import type { InspectorAdapters } from '../../hooks/use_inspector'; import { type DiscoverMainContentProps, DiscoverMainContent } from './discover_main_content'; import { ResetSearchButton } from './reset_search_button'; +import { useAppStateSelector } from '../../services/discover_app_state_container'; export interface DiscoverHistogramLayoutProps extends DiscoverMainContentProps { resetSavedSearch: () => void; @@ -35,19 +36,13 @@ export const DiscoverHistogramLayout = ({ inspectorAdapters, ...mainContentProps }: DiscoverHistogramLayoutProps) => { - const commonProps = { - dataView, - stateContainer, - savedSearchData$: stateContainer.dataState.data$, - }; const searchSessionId = useObservable(stateContainer.searchSessionManager.searchSessionId$); - - const { hideChart, setUnifiedHistogramApi } = useDiscoverHistogram({ + const hideChart = useAppStateSelector((state) => state.hideChart); + const unifiedHistogramProps = useDiscoverHistogram({ + stateContainer, inspectorAdapters, - savedSearchFetch$: stateContainer.dataState.fetch$, - searchSessionId, + hideChart, isPlainRecord, - ...commonProps, }); // Initialized when the first search has been requested or @@ -58,7 +53,10 @@ export const DiscoverHistogramLayout = ({ return ( : undefined @@ -66,8 +64,9 @@ export const DiscoverHistogramLayout = ({ css={histogramLayoutCss} > { const renderUseDiscoverHistogram = async ({ stateContainer = getStateContainer(), - searchSessionId = '123', inspectorAdapters = { requests: new RequestAdapter() }, - totalHits$ = new BehaviorSubject({ - fetchStatus: FetchStatus.COMPLETE, - result: Number(esHits.length), - }) as DataTotalHits$, - main$ = new BehaviorSubject({ - fetchStatus: FetchStatus.COMPLETE, - recordRawType: RecordRawType.DOCUMENT, - foundDocuments: true, - }) as DataMain$, - savedSearchFetch$ = new Subject() as DataFetch$, - documents$ = new BehaviorSubject({ - fetchStatus: FetchStatus.COMPLETE, - result: esHits.map((esHit) => buildDataTableRecord(esHit, dataViewWithTimefieldMock)), - }) as DataDocuments$, + hideChart = false, isPlainRecord = false, }: { stateContainer?: DiscoverStateContainer; - searchSessionId?: string; inspectorAdapters?: InspectorAdapters; - totalHits$?: DataTotalHits$; - main$?: DataMain$; - savedSearchFetch$?: DataFetch$; - documents$?: DataDocuments$; + hideChart?: boolean; isPlainRecord?: boolean; } = {}) => { - const availableFields$ = new BehaviorSubject({ - fetchStatus: FetchStatus.COMPLETE, - fields: [] as string[], - }) as AvailableFields$; - - const savedSearchData$ = { - main$, - documents$, - totalHits$, - availableFields$, - }; - const initialProps = { stateContainer, - savedSearchData$, - savedSearchFetch$, - dataView: dataViewWithTimefieldMock, inspectorAdapters, - searchSessionId, + hideChart, isPlainRecord, }; @@ -170,32 +125,17 @@ describe('useDiscoverHistogram', () => { }; describe('initialization', () => { - it('should pass the expected parameters to initialize', async () => { + it('should return the expected parameters from getCreationOptions', async () => { const { hook } = await renderUseDiscoverHistogram(); - const api = createMockUnifiedHistogramApi(); - let params: UnifiedHistogramInitializeOptions | undefined; - api.initialize = jest.fn((p) => { - params = p; - }); - act(() => { - hook.result.current.setUnifiedHistogramApi(api); - }); - expect(api.initialize).toHaveBeenCalled(); + const params = hook.result.current.getCreationOptions(); expect(params?.localStorageKeyPrefix).toBe('discover'); expect(params?.disableAutoFetching).toBe(true); expect(Object.keys(params?.initialState ?? {})).toEqual([ - 'dataView', - 'query', - 'filters', - 'timeRange', 'chartHidden', 'timeInterval', - 'columns', 'breakdownField', - 'searchSessionId', 'totalHitsStatus', 'totalHitsResult', - 'requestAdapter', ]); }); }); @@ -206,12 +146,12 @@ describe('useDiscoverHistogram', () => { }); it('should subscribe to state changes', async () => { const { hook } = await renderUseDiscoverHistogram(); - const api = createMockUnifiedHistogramApi({ initialized: true }); + const api = createMockUnifiedHistogramApi(); jest.spyOn(api.state$, 'subscribe'); act(() => { - hook.result.current.setUnifiedHistogramApi(api); + hook.result.current.ref(api); }); - expect(api.state$.subscribe).toHaveBeenCalledTimes(4); + expect(api.state$.subscribe).toHaveBeenCalledTimes(3); }); it('should sync Unified Histogram state with the state container', async () => { @@ -225,12 +165,11 @@ describe('useDiscoverHistogram', () => { breakdownField: 'test', totalHitsStatus: UnifiedHistogramFetchStatus.loading, totalHitsResult: undefined, - dataView: dataViewWithTimefieldMock, } as unknown as UnifiedHistogramState; - const api = createMockUnifiedHistogramApi({ initialized: true }); + const api = createMockUnifiedHistogramApi(); api.state$ = new BehaviorSubject({ ...state, lensRequestAdapter }); act(() => { - hook.result.current.setUnifiedHistogramApi(api); + hook.result.current.ref(api); }); expect(inspectorAdapters.lensRequests).toBe(lensRequestAdapter); expect(stateContainer.appState.update).toHaveBeenCalledWith({ @@ -250,12 +189,11 @@ describe('useDiscoverHistogram', () => { breakdownField: containerState.breakdownField, totalHitsStatus: UnifiedHistogramFetchStatus.loading, totalHitsResult: undefined, - dataView: dataViewWithTimefieldMock, } as unknown as UnifiedHistogramState; - const api = createMockUnifiedHistogramApi({ initialized: true }); + const api = createMockUnifiedHistogramApi(); api.state$ = new BehaviorSubject(state); act(() => { - hook.result.current.setUnifiedHistogramApi(api); + hook.result.current.ref(api); }); expect(stateContainer.appState.update).not.toHaveBeenCalled(); }); @@ -263,11 +201,8 @@ describe('useDiscoverHistogram', () => { it('should sync the state container state with Unified Histogram', async () => { const stateContainer = getStateContainer(); const { hook } = await renderUseDiscoverHistogram({ stateContainer }); - const api = createMockUnifiedHistogramApi({ initialized: true }); + const api = createMockUnifiedHistogramApi(); let params: Partial = {}; - api.setRequestParams = jest.fn((p) => { - params = { ...params, ...p }; - }); api.setTotalHits = jest.fn((p) => { params = { ...params, ...p }; }); @@ -281,20 +216,13 @@ describe('useDiscoverHistogram', () => { params = { ...params, breakdownField }; }); act(() => { - hook.result.current.setUnifiedHistogramApi(api); + hook.result.current.ref(api); }); - expect(api.setRequestParams).toHaveBeenCalled(); expect(api.setTotalHits).toHaveBeenCalled(); expect(api.setChartHidden).toHaveBeenCalled(); expect(api.setTimeInterval).toHaveBeenCalled(); expect(api.setBreakdownField).toHaveBeenCalled(); expect(Object.keys(params ?? {})).toEqual([ - 'dataView', - 'query', - 'filters', - 'timeRange', - 'searchSessionId', - 'requestAdapter', 'totalHitsStatus', 'totalHitsResult', 'chartHidden', @@ -313,12 +241,11 @@ describe('useDiscoverHistogram', () => { breakdownField: containerState.breakdownField, totalHitsStatus: UnifiedHistogramFetchStatus.loading, totalHitsResult: undefined, - dataView: dataViewWithTimefieldMock, } as unknown as UnifiedHistogramState; - const api = createMockUnifiedHistogramApi({ initialized: true }); + const api = createMockUnifiedHistogramApi(); let params: Partial = {}; - api.setRequestParams = jest.fn((p) => { - params = { ...params, ...p }; + api.setChartHidden = jest.fn((chartHidden) => { + params = { ...params, chartHidden }; }); api.setTotalHits = jest.fn((p) => { params = { ...params, ...p }; @@ -326,20 +253,15 @@ describe('useDiscoverHistogram', () => { const subject$ = new BehaviorSubject(state); api.state$ = subject$; act(() => { - hook.result.current.setUnifiedHistogramApi(api); + hook.result.current.ref(api); }); expect(Object.keys(params ?? {})).toEqual([ - 'dataView', - 'query', - 'filters', - 'timeRange', - 'searchSessionId', - 'requestAdapter', 'totalHitsStatus', 'totalHitsResult', + 'chartHidden', ]); params = {}; - hook.rerender({ ...initialProps, searchSessionId: '321' }); + hook.rerender({ ...initialProps, hideChart: true }); act(() => { subject$.next({ ...state, @@ -347,28 +269,12 @@ describe('useDiscoverHistogram', () => { totalHitsResult: 100, }); }); - expect(Object.keys(params ?? {})).toEqual([ - 'dataView', - 'query', - 'filters', - 'timeRange', - 'searchSessionId', - 'requestAdapter', - ]); + expect(Object.keys(params ?? {})).toEqual(['chartHidden']); }); it('should update total hits when the total hits state changes', async () => { - const totalHits$ = new BehaviorSubject({ - fetchStatus: FetchStatus.LOADING, - result: undefined, - }) as DataTotalHits$; - const main$ = new BehaviorSubject({ - fetchStatus: FetchStatus.COMPLETE, - recordRawType: RecordRawType.DOCUMENT, - foundDocuments: true, - }) as DataMain$; const stateContainer = getStateContainer(); - const { hook } = await renderUseDiscoverHistogram({ stateContainer, totalHits$, main$ }); + const { hook } = await renderUseDiscoverHistogram({ stateContainer }); const containerState = stateContainer.appState.getState(); const state = { timeInterval: containerState.interval, @@ -376,22 +282,27 @@ describe('useDiscoverHistogram', () => { breakdownField: containerState.breakdownField, totalHitsStatus: UnifiedHistogramFetchStatus.loading, totalHitsResult: undefined, - dataView: dataViewWithTimefieldMock, } as unknown as UnifiedHistogramState; - const api = createMockUnifiedHistogramApi({ initialized: true }); + const api = createMockUnifiedHistogramApi(); api.state$ = new BehaviorSubject({ ...state, totalHitsStatus: UnifiedHistogramFetchStatus.complete, totalHitsResult: 100, }); + expect(stateContainer.dataState.data$.totalHits$.value).not.toEqual({ + fetchStatus: FetchStatus.COMPLETE, + result: 100, + recordRawType: stateContainer.dataState.data$.totalHits$.value.recordRawType, + }); act(() => { - hook.result.current.setUnifiedHistogramApi(api); + hook.result.current.ref(api); }); - expect(totalHits$.value).toEqual({ + expect(stateContainer.dataState.data$.totalHits$.value).toEqual({ fetchStatus: FetchStatus.COMPLETE, result: 100, + recordRawType: stateContainer.dataState.data$.totalHits$.value.recordRawType, }); - expect(mockCheckHitCount).toHaveBeenCalledWith(main$, 100); + expect(mockCheckHitCount).toHaveBeenCalledWith(stateContainer.dataState.data$.main$, 100); }); it('should not update total hits when the total hits state changes to an error', async () => { @@ -408,12 +319,8 @@ describe('useDiscoverHistogram', () => { }; mockData.query.getState = () => mockQueryState; - const totalHits$ = new BehaviorSubject({ - fetchStatus: FetchStatus.UNINITIALIZED, - result: undefined, - }) as DataTotalHits$; const stateContainer = getStateContainer(); - const { hook } = await renderUseDiscoverHistogram({ stateContainer, totalHits$ }); + const { hook } = await renderUseDiscoverHistogram({ stateContainer }); const containerState = stateContainer.appState.getState(); const error = new Error('test'); const state = { @@ -422,21 +329,26 @@ describe('useDiscoverHistogram', () => { breakdownField: containerState.breakdownField, totalHitsStatus: UnifiedHistogramFetchStatus.loading, totalHitsResult: undefined, - dataView: dataViewWithTimefieldMock, } as unknown as UnifiedHistogramState; - const api = createMockUnifiedHistogramApi({ initialized: true }); + const api = createMockUnifiedHistogramApi(); api.state$ = new BehaviorSubject({ ...state, totalHitsStatus: UnifiedHistogramFetchStatus.error, totalHitsResult: error, }); + expect(stateContainer.dataState.data$.totalHits$.value).not.toEqual({ + fetchStatus: FetchStatus.ERROR, + error, + recordRawType: stateContainer.dataState.data$.totalHits$.value.recordRawType, + }); act(() => { - hook.result.current.setUnifiedHistogramApi(api); + hook.result.current.ref(api); }); - expect(sendErrorTo).toHaveBeenCalledWith(totalHits$); - expect(totalHits$.value).toEqual({ + expect(sendErrorTo).toHaveBeenCalledWith(stateContainer.dataState.data$.totalHits$); + expect(stateContainer.dataState.data$.totalHits$.value).toEqual({ fetchStatus: FetchStatus.ERROR, error, + recordRawType: stateContainer.dataState.data$.totalHits$.value.recordRawType, }); expect(mockCheckHitCount).not.toHaveBeenCalled(); }); @@ -448,10 +360,12 @@ describe('useDiscoverHistogram', () => { reset: boolean; searchSessionId: string; }>(); - const { hook } = await renderUseDiscoverHistogram({ savedSearchFetch$ }); - const api = createMockUnifiedHistogramApi({ initialized: true }); + const stateContainer = getStateContainer(); + stateContainer.dataState.fetch$ = savedSearchFetch$; + const { hook } = await renderUseDiscoverHistogram({ stateContainer }); + const api = createMockUnifiedHistogramApi(); act(() => { - hook.result.current.setUnifiedHistogramApi(api); + hook.result.current.ref(api); }); expect(api.refetch).not.toHaveBeenCalled(); act(() => { @@ -461,21 +375,22 @@ describe('useDiscoverHistogram', () => { }); it('should skip the next refetch when hideChart changes from true to false', async () => { - const stateContainer = getStateContainer(); const savedSearchFetch$ = new Subject<{ reset: boolean; searchSessionId: string; }>(); - const { hook } = await renderUseDiscoverHistogram({ stateContainer, savedSearchFetch$ }); - const api = createMockUnifiedHistogramApi({ initialized: true }); + const stateContainer = getStateContainer(); + stateContainer.dataState.fetch$ = savedSearchFetch$; + const { hook, initialProps } = await renderUseDiscoverHistogram({ stateContainer }); + const api = createMockUnifiedHistogramApi(); act(() => { - hook.result.current.setUnifiedHistogramApi(api); + hook.result.current.ref(api); }); act(() => { - stateContainer.appState.update({ hideChart: true }); + hook.rerender({ ...initialProps, hideChart: true }); }); act(() => { - stateContainer.appState.update({ hideChart: false }); + hook.rerender({ ...initialProps, hideChart: false }); }); act(() => { savedSearchFetch$.next({ reset: false, searchSessionId: '1234' }); diff --git a/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts b/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts index 46d3ef314b337..50d3edefcd67c 100644 --- a/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts +++ b/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts @@ -6,17 +6,16 @@ * Side Public License, v 1. */ -import type { DataView } from '@kbn/data-views-plugin/common'; import { useQuerySubscriber } from '@kbn/unified-field-list-plugin/public'; import { UnifiedHistogramApi, UnifiedHistogramFetchStatus, - UnifiedHistogramInitializedApi, UnifiedHistogramState, } from '@kbn/unified-histogram-plugin/public'; import { isEqual } from 'lodash'; import { useCallback, useEffect, useRef, useMemo, useState } from 'react'; -import { distinctUntilChanged, filter, map, Observable, skip } from 'rxjs'; +import { distinctUntilChanged, filter, map, Observable } from 'rxjs'; +import useObservable from 'react-use/lib/useObservable'; import type { Suggestion } from '@kbn/lens-plugin/public'; import useLatest from 'react-use/lib/useLatest'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; @@ -24,97 +23,57 @@ import { getUiActions } from '../../../../kibana_services'; import { FetchStatus } from '../../../types'; import { useDataState } from '../../hooks/use_data_state'; import type { InspectorAdapters } from '../../hooks/use_inspector'; -import type { - DataDocuments$, - DataFetch$, - SavedSearchData, -} from '../../services/discover_data_state_container'; +import type { DataDocuments$ } from '../../services/discover_data_state_container'; import { checkHitCount, sendErrorTo } from '../../hooks/use_saved_search_messages'; import { useAppStateSelector } from '../../services/discover_app_state_container'; import type { DiscoverStateContainer } from '../../services/discover_state'; import { addLog } from '../../../../utils/add_log'; +import { useInternalStateSelector } from '../../services/discover_internal_state_container'; export interface UseDiscoverHistogramProps { stateContainer: DiscoverStateContainer; - savedSearchData$: SavedSearchData; - dataView: DataView; inspectorAdapters: InspectorAdapters; - savedSearchFetch$: DataFetch$; - searchSessionId: string | undefined; + hideChart: boolean | undefined; isPlainRecord: boolean; } export const useDiscoverHistogram = ({ stateContainer, - savedSearchData$, - dataView, inspectorAdapters, - savedSearchFetch$, - searchSessionId, + hideChart, isPlainRecord, }: UseDiscoverHistogramProps) => { const services = useDiscoverServices(); - const timefilter = services.data.query.timefilter.timefilter; + const savedSearchData$ = stateContainer.dataState.data$; /** * API initialization */ - const [unifiedHistogram, setUnifiedHistogram] = useState(); - - const setUnifiedHistogramApi = useCallback( - (api: UnifiedHistogramApi | null) => { - if (!api) { - return; - } + const [unifiedHistogram, ref] = useState(); - if (api.initialized) { - setUnifiedHistogram(api); - } else { - const { - hideChart: chartHidden, - interval: timeInterval, - breakdownField, - columns, - } = stateContainer.appState.getState(); - - const { fetchStatus: totalHitsStatus, result: totalHitsResult } = - savedSearchData$.totalHits$.getValue(); - - const { query, filters, time: timeRange } = services.data.query.getState(); - - api.initialize({ - services: { ...services, uiActions: getUiActions() }, - localStorageKeyPrefix: 'discover', - disableAutoFetching: true, - getRelativeTimeRange: timefilter.getTime, - initialState: { - dataView, - query, - filters, - timeRange, - chartHidden, - timeInterval, - columns, - breakdownField, - searchSessionId, - totalHitsStatus: totalHitsStatus.toString() as UnifiedHistogramFetchStatus, - totalHitsResult, - requestAdapter: inspectorAdapters.requests, - }, - }); - } - }, - [ - dataView, - inspectorAdapters.requests, - savedSearchData$.totalHits$, - searchSessionId, - services, - stateContainer.appState, - timefilter.getTime, - ] - ); + const getCreationOptions = useCallback(() => { + const { + hideChart: chartHidden, + interval: timeInterval, + breakdownField, + } = stateContainer.appState.getState(); + + const { fetchStatus: totalHitsStatus, result: totalHitsResult } = + savedSearchData$.totalHits$.getValue(); + + return { + localStorageKeyPrefix: 'discover', + disableAutoFetching: true, + initialState: { + chartHidden, + timeInterval, + breakdownField, + totalHitsStatus: totalHitsStatus.toString() as UnifiedHistogramFetchStatus, + totalHitsResult, + }, + }; + }, [savedSearchData$.totalHits$, stateContainer.appState]); /** * Sync Unified Histogram state with Discover state @@ -124,8 +83,12 @@ export const useDiscoverHistogram = ({ const subscription = createStateSyncObservable(unifiedHistogram?.state$)?.subscribe((state) => { inspectorAdapters.lensRequests = state.lensRequestAdapter; - const { hideChart, interval, breakdownField } = stateContainer.appState.getState(); - const oldState = { hideChart, interval, breakdownField }; + const appState = stateContainer.appState.getState(); + const oldState = { + hideChart: appState.hideChart, + interval: appState.interval, + breakdownField: appState.breakdownField, + }; const newState = { hideChart: state.chartHidden, interval: state.timeInterval, @@ -140,32 +103,7 @@ export const useDiscoverHistogram = ({ return () => { subscription?.unsubscribe(); }; - }, [inspectorAdapters, stateContainer, unifiedHistogram]); - - /** - * Update Unified Histgoram request params - */ - const { query, filters } = useQuerySubscriber({ data: services.data }); - const timeRange = timefilter.getAbsoluteTime(); - - useEffect(() => { - unifiedHistogram?.setRequestParams({ - dataView, - query, - filters, - timeRange, - searchSessionId, - requestAdapter: inspectorAdapters.requests, - }); - }, [ - dataView, - filters, - inspectorAdapters.requests, - query, - searchSessionId, - timeRange, - unifiedHistogram, - ]); + }, [inspectorAdapters, stateContainer.appState, unifiedHistogram?.state$]); /** * Override Unified Histgoram total hits with Discover partial results @@ -193,8 +131,6 @@ export const useDiscoverHistogram = ({ * Sync URL query params with Unified Histogram */ - const hideChart = useAppStateSelector((state) => state.hideChart); - useEffect(() => { if (typeof hideChart === 'boolean') { unifiedHistogram?.setChartHidden(hideChart); @@ -221,18 +157,15 @@ export const useDiscoverHistogram = ({ // Update the columns only when documents are fetched so the Lens suggestions // don't constantly change when the user modifies the table columns - useEffect(() => { - const subscription = createDocumentsFetchedObservable( - stateContainer.dataState.data$.documents$ - ).subscribe(({ textBasedQueryColumns }) => { - const columns = textBasedQueryColumns?.map(({ name }) => name) ?? []; - unifiedHistogram?.setColumns(columns); - }); + const columnsObservable = useMemo( + () => createColumnsObservable(savedSearchData$.documents$), + [savedSearchData$.documents$] + ); - return () => { - subscription.unsubscribe(); - }; - }, [stateContainer.appState, stateContainer.dataState.data$.documents$, unifiedHistogram]); + const columns = useObservable( + columnsObservable, + savedSearchData$.documents$.getValue().textBasedQueryColumns?.map(({ name }) => name) ?? [] + ); /** * Total hits @@ -284,10 +217,22 @@ export const useDiscoverHistogram = ({ unifiedHistogram?.state$, ]); + /** + * Request params + */ + const { query, filters } = useQuerySubscriber({ data: services.data }); + const timefilter = services.data.query.timefilter.timefilter; + const timeRange = timefilter.getAbsoluteTime(); + const relativeTimeRange = useObservable( + timefilter.getTimeUpdate$().pipe(map(() => timefilter.getTime())), + timefilter.getTime() + ); + /** * Data fetching */ + const savedSearchFetch$ = stateContainer.dataState.fetch$; const skipDiscoverRefetch = useRef(); const skipLensSuggestionRefetch = useRef(); const usingLensSuggestion = useLatest(isPlainRecord && !hideChart); @@ -343,20 +288,34 @@ export const useDiscoverHistogram = ({ // When the data view or query changes, which will trigger a current suggestion change, // skip the next refetch since we want to wait for the columns to update first, which // doesn't happen until after the documents are fetched + const dataViewId = useInternalStateSelector((state) => state.dataView?.id); + const skipFetchParams = useRef({ dataViewId, query }); + useEffect(() => { - const subscription = createSkipFetchObservable(unifiedHistogram?.state$)?.subscribe(() => { - if (usingLensSuggestion.current) { - skipLensSuggestionRefetch.current = true; - skipDiscoverRefetch.current = true; - } - }); + const newSkipFetchParams = { dataViewId, query }; - return () => { - subscription?.unsubscribe(); - }; - }, [unifiedHistogram?.state$, usingLensSuggestion]); + if (isEqual(skipFetchParams.current, newSkipFetchParams)) { + return; + } + + skipFetchParams.current = newSkipFetchParams; + + if (usingLensSuggestion.current) { + skipLensSuggestionRefetch.current = true; + skipDiscoverRefetch.current = true; + } + }, [dataViewId, query, usingLensSuggestion]); - return { hideChart, setUnifiedHistogramApi }; + return { + ref, + getCreationOptions, + services: { ...services, uiActions: getUiActions() }, + query, + filters, + timeRange, + relativeTimeRange, + columns, + }; }; const createStateSyncObservable = (state$?: Observable) => { @@ -376,10 +335,11 @@ const createStateSyncObservable = (state$?: Observable) = ); }; -const createDocumentsFetchedObservable = (documents$: DataDocuments$) => { +const createColumnsObservable = (documents$: DataDocuments$) => { return documents$.pipe( distinctUntilChanged((prev, curr) => prev.fetchStatus === curr.fetchStatus), - filter(({ fetchStatus }) => fetchStatus === FetchStatus.COMPLETE) + filter(({ fetchStatus }) => fetchStatus === FetchStatus.COMPLETE), + map(({ textBasedQueryColumns }) => textBasedQueryColumns?.map(({ name }) => name) ?? []) ); }; @@ -396,11 +356,3 @@ const createCurrentSuggestionObservable = (state$?: Observable) => { - return state$?.pipe( - map((state) => [state.dataView.id, state.query]), - distinctUntilChanged(isEqual), - skip(1) - ); -}; diff --git a/src/plugins/home/public/application/components/guided_onboarding/getting_started.tsx b/src/plugins/home/public/application/components/guided_onboarding/getting_started.tsx index e7e24fda9676a..e683b4f4e1bcc 100644 --- a/src/plugins/home/public/application/components/guided_onboarding/getting_started.tsx +++ b/src/plugins/home/public/application/components/guided_onboarding/getting_started.tsx @@ -205,7 +205,7 @@ export const GettingStarted = () => {

{title}

diff --git a/src/plugins/presentation_util/public/components/data_view_picker/data_view_picker.tsx b/src/plugins/presentation_util/public/components/data_view_picker/data_view_picker.tsx index 756fc9c525bb1..fae805cd49ebc 100644 --- a/src/plugins/presentation_util/public/components/data_view_picker/data_view_picker.tsx +++ b/src/plugins/presentation_util/public/components/data_view_picker/data_view_picker.tsx @@ -31,7 +31,7 @@ export function DataViewPicker({ selectedDataViewId?: string; trigger: DataViewTriggerProps; onChangeDataViewId: (newId: string) => void; - selectableProps?: EuiSelectableProps; + selectableProps?: Partial; }) { const [isPopoverOpen, setPopoverIsOpen] = useState(false); @@ -61,58 +61,56 @@ export function DataViewPicker({ }; return ( - <> - setPopoverIsOpen(false)} - display="block" - panelPaddingSize="s" - ownFocus - panelClassName="presDataViewPicker__panel" + setPopoverIsOpen(false)} + display="block" + panelPaddingSize="s" + ownFocus + panelClassName="presDataViewPicker__panel" + > + + {i18n.translate('presentationUtil.dataViewPicker.changeDataViewTitle', { + defaultMessage: 'Data view', + })} + + + {...selectableProps} + searchable + singleSelection="always" + options={dataViews.map(({ name, id, title }) => ({ + key: id, + label: name ?? title, + value: id, + 'data-test-subj': `data-view-picker-${name ?? title}`, + checked: id === selectedDataViewId ? 'on' : undefined, + }))} + onChange={(choices) => { + const choice = choices.find(({ checked }) => checked) as unknown as { + value: string; + }; + onChangeDataViewId(choice.value); + setPopoverIsOpen(false); + }} + searchProps={{ + compressed: true, + ...(selectableProps ? selectableProps.searchProps : undefined), + }} > - - {i18n.translate('presentationUtil.dataViewPicker.changeDataViewTitle', { - defaultMessage: 'Data view', - })} - - - {...selectableProps} - searchable - singleSelection="always" - options={dataViews.map(({ name, id, title }) => ({ - key: id, - label: name ?? title, - value: id, - 'data-test-subj': `data-view-picker-${name ?? title}`, - checked: id === selectedDataViewId ? 'on' : undefined, - }))} - onChange={(choices) => { - const choice = choices.find(({ checked }) => checked) as unknown as { - value: string; - }; - onChangeDataViewId(choice.value); - setPopoverIsOpen(false); - }} - searchProps={{ - compressed: true, - ...(selectableProps ? selectableProps.searchProps : undefined), - }} - > - {(list, search) => ( - <> - {search} - {list} - - )} - - - + {(list, search) => ( + <> + {search} + {list} + + )} + + ); } diff --git a/src/plugins/presentation_util/public/components/field_picker/field_picker.scss b/src/plugins/presentation_util/public/components/field_picker/field_picker.scss index bbb3e8eb44be3..cb81739118249 100644 --- a/src/plugins/presentation_util/public/components/field_picker/field_picker.scss +++ b/src/plugins/presentation_util/public/components/field_picker/field_picker.scss @@ -1,4 +1,14 @@ .presFieldPickerFieldButtonActive { background-color: transparentize($euiColorPrimary, .9); +} + +.fieldPickerSelectable { + height: $euiSizeXXL * 9; // 40 * 9 = 360px + + &.fieldPickerSelectableLoading { + .euiSelectableMessage { + height: 100%; + } + } } \ No newline at end of file diff --git a/src/plugins/presentation_util/public/components/field_picker/field_picker.tsx b/src/plugins/presentation_util/public/components/field_picker/field_picker.tsx index 3d6a42d7211a8..20605d374d0fc 100644 --- a/src/plugins/presentation_util/public/components/field_picker/field_picker.tsx +++ b/src/plugins/presentation_util/public/components/field_picker/field_picker.tsx @@ -12,7 +12,13 @@ import React, { useEffect, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { FieldIcon } from '@kbn/react-field'; -import { EuiSelectable, EuiSelectableOption, EuiSpacer } from '@elastic/eui'; +import { + EuiFormRow, + EuiSelectable, + EuiSelectableOption, + EuiSelectableProps, + EuiSpacer, +} from '@elastic/eui'; import { DataView, DataViewField } from '@kbn/data-views-plugin/common'; import { FieldTypeFilter } from './field_type_filter'; @@ -24,6 +30,7 @@ export interface FieldPickerProps { selectedFieldName?: string; filterPredicate?: (f: DataViewField) => boolean; onSelectField?: (selectedField: DataViewField) => void; + selectableProps?: Partial; } export const FieldPicker = ({ @@ -31,6 +38,7 @@ export const FieldPicker = ({ onSelectField, filterPredicate, selectedFieldName, + selectableProps, }: FieldPickerProps) => { const [typesFilter, setTypesFilter] = useState([]); const [fieldSelectableOptions, setFieldSelectableOptions] = useState([]); @@ -82,15 +90,22 @@ export const FieldPicker = ({ ); const fieldTypeFilter = ( - setTypesFilter(types)} - fieldTypesValue={typesFilter} - availableFieldTypes={uniqueTypes} - /> + + setTypesFilter(types)} + fieldTypesValue={typesFilter} + availableFieldTypes={uniqueTypes} + buttonProps={{ disabled: Boolean(selectableProps?.isLoading) }} + /> + ); return ( {(list, search) => ( <> diff --git a/src/plugins/presentation_util/public/components/field_picker/field_type_filter.tsx b/src/plugins/presentation_util/public/components/field_picker/field_type_filter.tsx index bb27137ce16c8..a17739cf8ccbd 100644 --- a/src/plugins/presentation_util/public/components/field_picker/field_type_filter.tsx +++ b/src/plugins/presentation_util/public/components/field_picker/field_type_filter.tsx @@ -18,6 +18,7 @@ import { EuiOutsideClickDetector, EuiFilterButton, EuiPopoverTitle, + EuiFilterButtonProps, } from '@elastic/eui'; import { FieldIcon } from '@kbn/react-field'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -27,14 +28,15 @@ import './field_type_filter.scss'; export interface Props { onFieldTypesChange: (value: string[]) => void; fieldTypesValue: string[]; - availableFieldTypes: string[]; + buttonProps?: Partial; } export function FieldTypeFilter({ onFieldTypesChange, fieldTypesValue, availableFieldTypes, + buttonProps, }: Props) { const [isPopoverOpen, setPopoverOpen] = useState(false); @@ -44,6 +46,7 @@ export function FieldTypeFilter({ const buttonContent = ( 0} @@ -61,7 +64,7 @@ export function FieldTypeFilter({ return ( {}} isDisabled={!isPopoverOpen}> - + } + > + +
+); +``` + +## Advanced Usage ```tsx // Import the container component and API contract import { UnifiedHistogramContainer, - type UnifiedHistogramInitializedApi, + type UnifiedHistogramApi, } from '@kbn/unified-histogram-plugin/public'; // Import modules required for your application @@ -18,6 +73,7 @@ import { useResizeRef, useCallbacks, useRequestParams, + useStateParams, useManualRefetch, MyLayout, MyButton, @@ -26,75 +82,48 @@ import { const services = useServices(); const resizeRef = useResizeRef(); const { onChartHiddenChange, onLensRequestAdapterChange } = useCallbacks(); +const { chartHidden, breakdownField } = useStateParams(); const { dataView, query, filters, timeRange, + relativeTimeRange, searchSessionId, requestAdapter, } = useRequestParams(); // Use a state variable instead of a ref to preserve reactivity when the API is updated -const [unifiedHistogram, setUnifiedHistogram] = useState(); - -// Create a callback to set unifiedHistogram, and initialize it if needed -const setUnifiedHistogramApi = useCallback((api: UnifiedHistogramApi | null) => { - // Ignore if the ref is null - if (!api) { - return; - } - - if (api.initialized) { - // Update our local reference to the API - setUnifiedHistogram(api); - } else { - // Initialize if not yet initialized - api.initialize({ - // Pass the required services to Unified Histogram - services, - // Optionally provide a local storage key prefix to save parts of the state, - // such as the chart hidden state and top panel height, to local storage - localStorageKeyPrefix: 'myApp', - // By default Unified Histogram will automatically refetch based on certain - // state changes, such as chart hidden and request params, but this can be - // disabled in favour of manual fetching if preferred. Note that an initial - // request is always triggered when first initialized, and when the chart - // changes from hidden to visible, Lens will automatically trigger a refetch - // regardless of what this property is set to - disableAutoFetching: true, - // If passing an absolute time range, provide a function to get the relative range - getRelativeTimeRange: services.data.query.timefilter.timefilter.getTime, - // At minimum the initial state requires a data view, but additional - // parameters can be passed to further customize the state - initialState: { - dataView, - query, - filters, - timeRange, - searchSessionId, - requestAdapter, - }, - }); - } -}, [...]); +const [unifiedHistogram, setUnifiedHistogram] = useState(); + +const getCreationOptions = useCallback(() => ({ + // Optionally provide a local storage key prefix to save parts of the state, + // such as the chart hidden state and top panel height, to local storage + localStorageKeyPrefix: 'myApp', + // By default Unified Histogram will automatically refetch based on certain + // state changes, such as chart hidden and request params, but this can be + // disabled in favour of manual fetching if preferred. Note that an initial + // request is always triggered when first initialized, and when the chart + // changes from hidden to visible, Lens will automatically trigger a refetch + // regardless of what this property is set to + disableAutoFetching: true, + // Customize the initial state in order to override the defaults + initialState: { chartHidden, breakdownField }, +}), [...]); // Manually refetch if disableAutoFetching is true useManualRefetch(() => { unifiedHistogram?.refetch(); }); -// Update the Unified Histogram state when our request params change +// Update the Unified Histogram state when our state params change useEffect(() => { - unifiedHistogram?.setRequestParams({ - dataView, - query, - filters, - timeRange, - searchSessionId, - requestAdapter, - }); -}, [...]); + unifiedHistogram?.setChartHidden(chartHidden); +}, [chartHidden]); + +useEffect(() => { + unifiedHistogram?.setBreakdownField(breakdownField); +}, [breakdownField]); // Listen for state changes if your application requires it useEffect(() => { @@ -124,12 +153,18 @@ useEffect(() => { return ( } > diff --git a/src/plugins/unified_histogram/public/chart/chart.tsx b/src/plugins/unified_histogram/public/chart/chart.tsx index 038c7c696ddf8..3c959776df2a5 100644 --- a/src/plugins/unified_histogram/public/chart/chart.tsx +++ b/src/plugins/unified_histogram/public/chart/chart.tsx @@ -56,6 +56,7 @@ export interface ChartProps { currentSuggestion?: Suggestion; allSuggestions?: Suggestion[]; timeRange?: TimeRange; + relativeTimeRange?: TimeRange; request?: UnifiedHistogramRequestContext; hits?: UnifiedHistogramHitsContext; chart?: UnifiedHistogramChartContext; @@ -66,7 +67,6 @@ export interface ChartProps { disableTriggers?: LensEmbeddableInput['disableTriggers']; disabledActions?: LensEmbeddableInput['disabledActions']; input$?: UnifiedHistogramInput$; - getRelativeTimeRange?: () => TimeRange; onResetChartHeight?: () => void; onChartHiddenChange?: (chartHidden: boolean) => void; onTimeIntervalChange?: (timeInterval: string) => void; @@ -87,6 +87,7 @@ export function Chart({ query: originalQuery, filters: originalFilters, timeRange: originalTimeRange, + relativeTimeRange: originalRelativeTimeRange, request, hits, chart, @@ -100,7 +101,6 @@ export function Chart({ disableTriggers, disabledActions, input$: originalInput$, - getRelativeTimeRange: originalGetRelativeTimeRange, onResetChartHeight, onChartHiddenChange, onTimeIntervalChange, @@ -214,15 +214,10 @@ export function Chart({ ] ); - const getRelativeTimeRange = useMemo( - () => originalGetRelativeTimeRange ?? (() => relativeTimeRange), - [originalGetRelativeTimeRange, relativeTimeRange] - ); - const onEditVisualization = useEditVisualization({ services, dataView, - getRelativeTimeRange, + relativeTimeRange: originalRelativeTimeRange ?? relativeTimeRange, lensAttributes, }); diff --git a/src/plugins/unified_histogram/public/chart/hooks/use_edit_visualization.test.ts b/src/plugins/unified_histogram/public/chart/hooks/use_edit_visualization.test.ts index 1d92db77dc376..8bd9e9161c837 100644 --- a/src/plugins/unified_histogram/public/chart/hooks/use_edit_visualization.test.ts +++ b/src/plugins/unified_histogram/public/chart/hooks/use_edit_visualization.test.ts @@ -39,7 +39,7 @@ describe('useEditVisualization', () => { useEditVisualization({ services: unifiedHistogramServicesMock, dataView: dataViewWithTimefieldMock, - getRelativeTimeRange: () => relativeTimeRange, + relativeTimeRange, lensAttributes, }) ); @@ -59,7 +59,7 @@ describe('useEditVisualization', () => { useEditVisualization({ services: unifiedHistogramServicesMock, dataView: { ...dataViewWithTimefieldMock, id: undefined } as DataView, - getRelativeTimeRange: () => ({ from: 'now-15m', to: 'now' }), + relativeTimeRange: { from: 'now-15m', to: 'now' }, lensAttributes: {} as unknown as TypedLensByValueInput['attributes'], }) ); @@ -73,7 +73,7 @@ describe('useEditVisualization', () => { useEditVisualization({ services: unifiedHistogramServicesMock, dataView: dataViewMock, - getRelativeTimeRange: () => ({ from: 'now-15m', to: 'now' }), + relativeTimeRange: { from: 'now-15m', to: 'now' }, lensAttributes: {} as unknown as TypedLensByValueInput['attributes'], }) ); @@ -93,7 +93,7 @@ describe('useEditVisualization', () => { useEditVisualization({ services: unifiedHistogramServicesMock, dataView, - getRelativeTimeRange: () => ({ from: 'now-15m', to: 'now' }), + relativeTimeRange: { from: 'now-15m', to: 'now' }, lensAttributes: {} as unknown as TypedLensByValueInput['attributes'], }) ); @@ -107,7 +107,7 @@ describe('useEditVisualization', () => { useEditVisualization({ services: unifiedHistogramServicesMock, dataView: dataViewWithTimefieldMock, - getRelativeTimeRange: () => ({ from: 'now-15m', to: 'now' }), + relativeTimeRange: { from: 'now-15m', to: 'now' }, lensAttributes: {} as unknown as TypedLensByValueInput['attributes'], }) ); diff --git a/src/plugins/unified_histogram/public/chart/hooks/use_edit_visualization.ts b/src/plugins/unified_histogram/public/chart/hooks/use_edit_visualization.ts index ba72f5bc9264e..c1b1f899ae756 100644 --- a/src/plugins/unified_histogram/public/chart/hooks/use_edit_visualization.ts +++ b/src/plugins/unified_histogram/public/chart/hooks/use_edit_visualization.ts @@ -19,12 +19,12 @@ const visualizeFieldTrigger: typeof VISUALIZE_FIELD_TRIGGER = 'VISUALIZE_FIELD_T export const useEditVisualization = ({ services, dataView, - getRelativeTimeRange, + relativeTimeRange, lensAttributes, }: { services: UnifiedHistogramServices; dataView: DataView; - getRelativeTimeRange: () => TimeRange; + relativeTimeRange?: TimeRange; lensAttributes: TypedLensByValueInput['attributes']; }) => { const [canVisualize, setCanVisualize] = useState(false); @@ -53,11 +53,11 @@ export const useEditVisualization = ({ return () => { services.lens.navigateToPrefilledEditor({ id: '', - timeRange: getRelativeTimeRange(), + timeRange: relativeTimeRange, attributes: lensAttributes, }); }; - }, [canVisualize, getRelativeTimeRange, lensAttributes, services.lens]); + }, [canVisualize, lensAttributes, relativeTimeRange, services.lens]); useEffect(() => { checkCanVisualize().then(setCanVisualize); diff --git a/src/plugins/unified_histogram/public/container/container.test.tsx b/src/plugins/unified_histogram/public/container/container.test.tsx index 1745769528851..ea6ef95db55fa 100644 --- a/src/plugins/unified_histogram/public/container/container.test.tsx +++ b/src/plugins/unified_histogram/public/container/container.test.tsx @@ -10,79 +10,70 @@ import { RequestAdapter } from '@kbn/inspector-plugin/common'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import React from 'react'; import { act } from 'react-dom/test-utils'; -import { UnifiedHistogramFetchStatus } from '../types'; +import { UnifiedHistogramLayout } from '../layout'; import { dataViewWithTimefieldMock } from '../__mocks__/data_view_with_timefield'; import { unifiedHistogramServicesMock } from '../__mocks__/services'; import { UnifiedHistogramApi, UnifiedHistogramContainer } from './container'; -import type { UnifiedHistogramState } from './services/state_service'; describe('UnifiedHistogramContainer', () => { - const initialState: UnifiedHistogramState = { - breakdownField: 'bytes', - chartHidden: false, - dataView: dataViewWithTimefieldMock, - filters: [], - lensRequestAdapter: new RequestAdapter(), - query: { language: 'kuery', query: '' }, - requestAdapter: new RequestAdapter(), - searchSessionId: '123', - timeInterval: 'auto', - timeRange: { from: 'now-15m', to: 'now' }, - topPanelHeight: 100, - totalHitsStatus: UnifiedHistogramFetchStatus.uninitialized, - totalHitsResult: undefined, - columns: [], - currentSuggestion: undefined, - }; - - it('should set ref', () => { + it('should initialize', async () => { let api: UnifiedHistogramApi | undefined; const setApi = (ref: UnifiedHistogramApi) => { api = ref; }; - mountWithIntl(); - expect(api).toBeDefined(); - }); - - it('should return null if not initialized', async () => { - const component = mountWithIntl(); - await new Promise((resolve) => setTimeout(resolve, 0)); - expect(component.update().isEmptyRender()).toBe(true); - }); - - it('should not return null if initialized', async () => { - const setApi = (api: UnifiedHistogramApi | null) => { - if (!api || api.initialized) { - return; - } - api?.initialize({ - services: unifiedHistogramServicesMock, - initialState, - }); - }; + const getCreationOptions = jest.fn(() => ({ initialState: { timeInterval: '42s' } })); const component = mountWithIntl( - + ); + expect(component.update().isEmptyRender()).toBe(true); await act(() => new Promise((resolve) => setTimeout(resolve, 0))); + component.update(); + expect(getCreationOptions).toHaveBeenCalled(); + expect(component.find(UnifiedHistogramLayout).prop('chart')?.timeInterval).toBe('42s'); expect(component.update().isEmptyRender()).toBe(false); + expect(api).toBeDefined(); }); - it('should update initialized property when initialized', async () => { + it('should trigger input$ when refetch is called', async () => { let api: UnifiedHistogramApi | undefined; const setApi = (ref: UnifiedHistogramApi) => { api = ref; }; - mountWithIntl(); - expect(api?.initialized).toBe(false); + const getCreationOptions = jest.fn(() => ({ disableAutoFetching: true })); + const component = mountWithIntl( + + ); + await act(() => new Promise((resolve) => setTimeout(resolve, 0))); + component.update(); + const input$ = component.find(UnifiedHistogramLayout).prop('input$'); + const inputSpy = jest.fn(); + input$?.subscribe(inputSpy); act(() => { - if (!api?.initialized) { - api?.initialize({ - services: unifiedHistogramServicesMock, - initialState, - }); - } + api?.refetch(); }); - await act(() => new Promise((resolve) => setTimeout(resolve, 0))); - expect(api?.initialized).toBe(true); + expect(inputSpy).toHaveBeenCalledTimes(1); + expect(inputSpy).toHaveBeenCalledWith({ type: 'refetch' }); }); }); diff --git a/src/plugins/unified_histogram/public/container/container.tsx b/src/plugins/unified_histogram/public/container/container.tsx index 3bfc7c5d69a57..0633e9d710966 100644 --- a/src/plugins/unified_histogram/public/container/container.tsx +++ b/src/plugins/unified_histogram/public/container/container.tsx @@ -6,12 +6,13 @@ * Side Public License, v 1. */ -import React, { forwardRef, useImperativeHandle, useMemo, useState } from 'react'; +import React, { forwardRef, useEffect, useImperativeHandle, useState } from 'react'; import { Subject } from 'rxjs'; import { pick } from 'lodash'; +import useMount from 'react-use/lib/useMount'; import { LensSuggestionsApi } from '@kbn/lens-plugin/public'; import { UnifiedHistogramLayout, UnifiedHistogramLayoutProps } from '../layout'; -import type { UnifiedHistogramInputMessage } from '../types'; +import type { UnifiedHistogramInputMessage, UnifiedHistogramRequestContext } from '../types'; import { createStateService, UnifiedHistogramStateOptions, @@ -19,61 +20,47 @@ import { } from './services/state_service'; import { useStateProps } from './hooks/use_state_props'; import { useStateSelector } from './utils/use_state_selector'; -import { - columnsSelector, - currentSuggestionSelector, - dataViewSelector, - filtersSelector, - querySelector, - timeRangeSelector, - topPanelHeightSelector, -} from './utils/state_selectors'; +import { topPanelHeightSelector, currentSuggestionSelector } from './utils/state_selectors'; type LayoutProps = Pick< UnifiedHistogramLayoutProps, - | 'services' - | 'disableAutoFetching' - | 'disableTriggers' - | 'disabledActions' - | 'getRelativeTimeRange' ->; - -/** - * The props exposed by the container - */ -export type UnifiedHistogramContainerProps = Pick< - UnifiedHistogramLayoutProps, - 'className' | 'resizeRef' | 'appendHitsCounter' | 'children' + 'disableAutoFetching' | 'disableTriggers' | 'disabledActions' >; /** * The options used to initialize the container */ -export type UnifiedHistogramInitializeOptions = UnifiedHistogramStateOptions & - Omit; +export type UnifiedHistogramCreationOptions = Omit & + LayoutProps; /** - * The uninitialized API exposed by the container + * The props exposed by the container */ -export interface UnifiedHistogramUninitializedApi { - /** - * Whether the container has been initialized - */ - initialized: false; - /** - * Initialize the container - */ - initialize: (options: UnifiedHistogramInitializeOptions) => void; -} +export type UnifiedHistogramContainerProps = { + getCreationOptions?: () => + | UnifiedHistogramCreationOptions + | Promise; + searchSessionId?: UnifiedHistogramRequestContext['searchSessionId']; + requestAdapter?: UnifiedHistogramRequestContext['adapter']; +} & Pick< + UnifiedHistogramLayoutProps, + | 'services' + | 'className' + | 'dataView' + | 'query' + | 'filters' + | 'timeRange' + | 'relativeTimeRange' + | 'columns' + | 'resizeRef' + | 'appendHitsCounter' + | 'children' +>; /** - * The initialized API exposed by the container + * The API exposed by the container */ -export type UnifiedHistogramInitializedApi = { - /** - * Whether the container has been initialized - */ - initialized: true; +export type UnifiedHistogramApi = { /** * Manually trigger a refetch of the data */ @@ -84,86 +71,69 @@ export type UnifiedHistogramInitializedApi = { | 'setChartHidden' | 'setTopPanelHeight' | 'setBreakdownField' - | 'setColumns' | 'setTimeInterval' - | 'setRequestParams' | 'setTotalHits' >; -/** - * The API exposed by the container - */ -export type UnifiedHistogramApi = UnifiedHistogramUninitializedApi | UnifiedHistogramInitializedApi; - export const UnifiedHistogramContainer = forwardRef< UnifiedHistogramApi, UnifiedHistogramContainerProps >((containerProps, ref) => { - const [initialized, setInitialized] = useState(false); const [layoutProps, setLayoutProps] = useState(); const [stateService, setStateService] = useState(); const [lensSuggestionsApi, setLensSuggestionsApi] = useState(); const [input$] = useState(() => new Subject()); - const api = useMemo( - () => ({ - initialized, - initialize: (options: UnifiedHistogramInitializeOptions) => { - const { - services, - disableAutoFetching, - disableTriggers, - disabledActions, - getRelativeTimeRange, - } = options; + const [api, setApi] = useState(); - // API helpers are loaded async from Lens - (async () => { - const apiHelper = await services.lens.stateHelperApi(); - setLensSuggestionsApi(() => apiHelper.suggestions); - })(); + // Expose the API to the parent component + useImperativeHandle(ref, () => api!, [api]); - setLayoutProps({ - services, - disableAutoFetching, - disableTriggers, - disabledActions, - getRelativeTimeRange, - }); - setStateService(createStateService(options)); - setInitialized(true); - }, + // Call for creation options once the container is mounted + useMount(async () => { + const { getCreationOptions, services } = containerProps; + const options = await getCreationOptions?.(); + const apiHelper = await services.lens.stateHelperApi(); + + setLayoutProps(pick(options, 'disableAutoFetching', 'disableTriggers', 'disabledActions')); + setStateService(createStateService({ services, ...options })); + setLensSuggestionsApi(() => apiHelper.suggestions); + }); + + // Initialize the API once the state service is available + useEffect(() => { + if (!stateService) { + return; + } + + setApi({ refetch: () => { input$.next({ type: 'refetch' }); }, ...pick( - stateService!, + stateService, 'state$', 'setChartHidden', 'setTopPanelHeight', 'setBreakdownField', - 'setColumns', 'setTimeInterval', - 'setRequestParams', 'setTotalHits' ), - }), - [initialized, input$, stateService] - ); - - // Expose the API to the parent component - useImperativeHandle(ref, () => api, [api]); + }); + }, [input$, stateService]); - const stateProps = useStateProps(stateService); - const dataView = useStateSelector(stateService?.state$, dataViewSelector); - const query = useStateSelector(stateService?.state$, querySelector); - const filters = useStateSelector(stateService?.state$, filtersSelector); - const timeRange = useStateSelector(stateService?.state$, timeRangeSelector); - const columns = useStateSelector(stateService?.state$, columnsSelector); + const { dataView, query, searchSessionId, requestAdapter } = containerProps; const currentSuggestion = useStateSelector(stateService?.state$, currentSuggestionSelector); const topPanelHeight = useStateSelector(stateService?.state$, topPanelHeightSelector); + const stateProps = useStateProps({ + stateService, + dataView, + query, + searchSessionId, + requestAdapter, + }); // Don't render anything until the container is initialized - if (!layoutProps || !dataView || !lensSuggestionsApi) { + if (!layoutProps || !lensSuggestionsApi || !api) { return null; } @@ -172,11 +142,6 @@ export const UnifiedHistogramContainer = forwardRef< {...containerProps} {...layoutProps} {...stateProps} - dataView={dataView} - query={query} - filters={filters} - timeRange={timeRange} - columns={columns} currentSuggestion={currentSuggestion} topPanelHeight={topPanelHeight} input$={input$} diff --git a/src/plugins/unified_histogram/public/container/hooks/use_state_props.test.ts b/src/plugins/unified_histogram/public/container/hooks/use_state_props.test.ts index fea0813119136..fcdd194410db0 100644 --- a/src/plugins/unified_histogram/public/container/hooks/use_state_props.test.ts +++ b/src/plugins/unified_histogram/public/container/hooks/use_state_props.test.ts @@ -27,18 +27,11 @@ describe('useStateProps', () => { const initialState: UnifiedHistogramState = { breakdownField: 'bytes', chartHidden: false, - dataView: dataViewWithTimefieldMock, - filters: [], lensRequestAdapter: new RequestAdapter(), - query: { language: 'kuery', query: '' }, - requestAdapter: new RequestAdapter(), - searchSessionId: '123', timeInterval: 'auto', - timeRange: { from: 'now-15m', to: 'now' }, topPanelHeight: 100, totalHitsStatus: UnifiedHistogramFetchStatus.uninitialized, totalHitsResult: undefined, - columns: [], currentSuggestion: undefined, }; @@ -51,7 +44,6 @@ describe('useStateProps', () => { jest.spyOn(stateService, 'setTopPanelHeight'); jest.spyOn(stateService, 'setBreakdownField'); jest.spyOn(stateService, 'setTimeInterval'); - jest.spyOn(stateService, 'setRequestParams'); jest.spyOn(stateService, 'setLensRequestAdapter'); jest.spyOn(stateService, 'setTotalHits'); jest.spyOn(stateService, 'setCurrentSuggestion'); @@ -60,7 +52,15 @@ describe('useStateProps', () => { it('should return the correct props', () => { const stateService = getStateService({ initialState }); - const { result } = renderHook(() => useStateProps(stateService)); + const { result } = renderHook(() => + useStateProps({ + stateService, + dataView: dataViewWithTimefieldMock, + query: { language: 'kuery', query: '' }, + requestAdapter: new RequestAdapter(), + searchSessionId: '123', + }) + ); expect(result.current).toMatchInlineSnapshot(` Object { "breakdown": Object { @@ -104,10 +104,16 @@ describe('useStateProps', () => { }); it('should return the correct props when an SQL query is used', () => { - const stateService = getStateService({ - initialState: { ...initialState, query: { sql: 'SELECT * FROM index' } }, - }); - const { result } = renderHook(() => useStateProps(stateService)); + const stateService = getStateService({ initialState }); + const { result } = renderHook(() => + useStateProps({ + stateService, + dataView: dataViewWithTimefieldMock, + query: { sql: 'SELECT * FROM index' }, + requestAdapter: new RequestAdapter(), + searchSessionId: '123', + }) + ); expect(result.current).toMatchInlineSnapshot(` Object { "breakdown": undefined, @@ -145,27 +151,37 @@ describe('useStateProps', () => { const stateService = getStateService({ initialState: { ...initialState, - query: { sql: 'SELECT * FROM index' }, currentSuggestion: currentSuggestionMock, }, }); - const { result } = renderHook(() => useStateProps(stateService)); + const { result } = renderHook(() => + useStateProps({ + stateService, + dataView: dataViewWithTimefieldMock, + query: { sql: 'SELECT * FROM index' }, + requestAdapter: new RequestAdapter(), + searchSessionId: '123', + }) + ); expect(result.current.chart).toStrictEqual({ hidden: false, timeInterval: 'auto' }); expect(result.current.breakdown).toBe(undefined); expect(result.current.isPlainRecord).toBe(true); }); it('should return the correct props when a rollup data view is used', () => { - const stateService = getStateService({ - initialState: { - ...initialState, + const stateService = getStateService({ initialState }); + const { result } = renderHook(() => + useStateProps({ + stateService, dataView: { ...dataViewWithTimefieldMock, type: DataViewType.ROLLUP, } as DataView, - }, - }); - const { result } = renderHook(() => useStateProps(stateService)); + query: { language: 'kuery', query: '' }, + requestAdapter: new RequestAdapter(), + searchSessionId: '123', + }) + ); expect(result.current).toMatchInlineSnapshot(` Object { "breakdown": undefined, @@ -197,10 +213,16 @@ describe('useStateProps', () => { }); it('should return the correct props when a non time based data view is used', () => { - const stateService = getStateService({ - initialState: { ...initialState, dataView: dataViewMock }, - }); - const { result } = renderHook(() => useStateProps(stateService)); + const stateService = getStateService({ initialState }); + const { result } = renderHook(() => + useStateProps({ + stateService, + dataView: dataViewMock, + query: { language: 'kuery', query: '' }, + requestAdapter: new RequestAdapter(), + searchSessionId: '123', + }) + ); expect(result.current).toMatchInlineSnapshot(` Object { "breakdown": undefined, @@ -233,7 +255,15 @@ describe('useStateProps', () => { it('should execute callbacks correctly', () => { const stateService = getStateService({ initialState }); - const { result } = renderHook(() => useStateProps(stateService)); + const { result } = renderHook(() => + useStateProps({ + stateService, + dataView: dataViewWithTimefieldMock, + query: { language: 'kuery', query: '' }, + requestAdapter: new RequestAdapter(), + searchSessionId: '123', + }) + ); const { onTopPanelHeightChange, onTimeIntervalChange, @@ -280,7 +310,15 @@ describe('useStateProps', () => { it('should clear lensRequestAdapter when chart is hidden', () => { const stateService = getStateService({ initialState }); - const hook = renderHook(() => useStateProps(stateService)); + const hook = renderHook(() => + useStateProps({ + stateService, + dataView: dataViewWithTimefieldMock, + query: { language: 'kuery', query: '' }, + requestAdapter: new RequestAdapter(), + searchSessionId: '123', + }) + ); (stateService.setLensRequestAdapter as jest.Mock).mockClear(); expect(stateService.setLensRequestAdapter).not.toHaveBeenCalled(); act(() => { @@ -292,13 +330,22 @@ describe('useStateProps', () => { it('should clear lensRequestAdapter when chart is undefined', () => { const stateService = getStateService({ initialState }); - const hook = renderHook(() => useStateProps(stateService)); + const initialProps = { + stateService, + dataView: dataViewWithTimefieldMock, + query: { language: 'kuery', query: '' }, + requestAdapter: new RequestAdapter(), + searchSessionId: '123', + }; + const hook = renderHook((props: Parameters[0]) => useStateProps(props), { + initialProps, + }); (stateService.setLensRequestAdapter as jest.Mock).mockClear(); expect(stateService.setLensRequestAdapter).not.toHaveBeenCalled(); - act(() => { - stateService.setRequestParams({ dataView: dataViewMock }); + hook.rerender({ + ...initialProps, + dataView: dataViewMock, }); - hook.rerender(); expect(stateService.setLensRequestAdapter).toHaveBeenLastCalledWith(undefined); }); }); diff --git a/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts b/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts index 8b63b6d91dd62..b9c4570cdecb9 100644 --- a/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts +++ b/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts @@ -6,31 +6,41 @@ * Side Public License, v 1. */ -import { DataViewField, DataViewType } from '@kbn/data-views-plugin/common'; -import { getAggregateQueryMode, isOfAggregateQueryType } from '@kbn/es-query'; +import { DataView, DataViewField, DataViewType } from '@kbn/data-views-plugin/common'; +import { + AggregateQuery, + getAggregateQueryMode, + isOfAggregateQueryType, + Query, +} from '@kbn/es-query'; +import type { RequestAdapter } from '@kbn/inspector-plugin/public'; import { useCallback, useEffect, useMemo } from 'react'; import { UnifiedHistogramChartLoadEvent, UnifiedHistogramFetchStatus } from '../../types'; import type { UnifiedHistogramStateService } from '../services/state_service'; import { breakdownFieldSelector, chartHiddenSelector, - dataViewSelector, - querySelector, - requestAdapterSelector, - searchSessionIdSelector, timeIntervalSelector, totalHitsResultSelector, totalHitsStatusSelector, } from '../utils/state_selectors'; import { useStateSelector } from '../utils/use_state_selector'; -export const useStateProps = (stateService: UnifiedHistogramStateService | undefined) => { +export const useStateProps = ({ + stateService, + dataView, + query, + searchSessionId, + requestAdapter, +}: { + stateService: UnifiedHistogramStateService | undefined; + dataView: DataView; + query: Query | AggregateQuery | undefined; + searchSessionId: string | undefined; + requestAdapter: RequestAdapter | undefined; +}) => { const breakdownField = useStateSelector(stateService?.state$, breakdownFieldSelector); const chartHidden = useStateSelector(stateService?.state$, chartHiddenSelector); - const dataView = useStateSelector(stateService?.state$, dataViewSelector); - const query = useStateSelector(stateService?.state$, querySelector); - const requestAdapter = useStateSelector(stateService?.state$, requestAdapterSelector); - const searchSessionId = useStateSelector(stateService?.state$, searchSessionIdSelector); const timeInterval = useStateSelector(stateService?.state$, timeIntervalSelector); const totalHitsResult = useStateSelector(stateService?.state$, totalHitsResultSelector); const totalHitsStatus = useStateSelector(stateService?.state$, totalHitsStatusSelector); diff --git a/src/plugins/unified_histogram/public/container/index.tsx b/src/plugins/unified_histogram/public/container/index.tsx index f692401c4fdbf..0110d8f099ae1 100644 --- a/src/plugins/unified_histogram/public/container/index.tsx +++ b/src/plugins/unified_histogram/public/container/index.tsx @@ -9,13 +9,12 @@ import { EuiDelayRender, EuiFlexGroup, EuiLoadingSpinner } from '@elastic/eui'; import { withSuspense } from '@kbn/shared-ux-utility'; import React, { lazy } from 'react'; +import type { UnifiedHistogramApi, UnifiedHistogramContainerProps } from './container'; export type { - UnifiedHistogramUninitializedApi, - UnifiedHistogramInitializedApi, UnifiedHistogramApi, UnifiedHistogramContainerProps, - UnifiedHistogramInitializeOptions, + UnifiedHistogramCreationOptions, } from './container'; export type { UnifiedHistogramState, UnifiedHistogramStateOptions } from './services/state_service'; export { @@ -32,10 +31,11 @@ const LazyUnifiedHistogramContainer = lazy(() => import('./container')); /** * A resizable layout component with two panels that renders a histogram with a hits * counter in the top panel, and a main display (data table, etc.) in the bottom panel. - * If all context props are left undefined, the layout will render in a single panel - * mode including only the main display. */ -export const UnifiedHistogramContainer = withSuspense( +export const UnifiedHistogramContainer = withSuspense< + UnifiedHistogramContainerProps, + UnifiedHistogramApi +>( LazyUnifiedHistogramContainer, { const initialState: UnifiedHistogramState = { breakdownField: 'bytes', chartHidden: false, - dataView: dataViewWithTimefieldMock, - filters: [], lensRequestAdapter: new RequestAdapter(), - query: { language: 'kuery', query: '' }, - requestAdapter: new RequestAdapter(), - searchSessionId: '123', timeInterval: 'auto', - timeRange: { from: 'now-15m', to: 'now' }, topPanelHeight: 100, totalHitsStatus: UnifiedHistogramFetchStatus.uninitialized, totalHitsResult: undefined, - columns: [], currentSuggestion: undefined, }; it('should initialize state with default values', () => { - const stateService = createStateService({ - services: unifiedHistogramServicesMock, - initialState: { - dataView: dataViewWithTimefieldMock, - }, - }); + const stateService = createStateService({ services: unifiedHistogramServicesMock }); let state: UnifiedHistogramState | undefined; stateService.state$.subscribe((s) => (state = s)); expect(state).toEqual({ breakdownField: undefined, chartHidden: false, - dataView: dataViewWithTimefieldMock, - filters: [], lensRequestAdapter: undefined, - query: unifiedHistogramServicesMock.data.query.queryString.getDefaultQuery(), - requestAdapter: undefined, - searchSessionId: undefined, timeInterval: 'auto', - timeRange: unifiedHistogramServicesMock.data.query.timefilter.timefilter.getTimeDefaults(), topPanelHeight: undefined, totalHitsResult: undefined, totalHitsStatus: UnifiedHistogramFetchStatus.uninitialized, - columns: [], currentSuggestion: undefined, allSuggestions: undefined, }); @@ -154,17 +132,6 @@ describe('UnifiedHistogramStateService', () => { stateService.setTimeInterval('test'); newState = { ...newState, timeInterval: 'test' }; expect(state).toEqual(newState); - const requestParams = { - dataView: dataViewMock, - filters: ['test'] as unknown as Filter[], - query: { language: 'kuery', query: 'test' }, - requestAdapter: undefined, - searchSessionId: '321', - timeRange: { from: 'now-30m', to: 'now' }, - }; - stateService.setRequestParams(requestParams); - newState = { ...newState, ...requestParams }; - expect(state).toEqual(newState); stateService.setLensRequestAdapter(undefined); newState = { ...newState, lensRequestAdapter: undefined }; expect(state).toEqual(newState); diff --git a/src/plugins/unified_histogram/public/container/services/state_service.ts b/src/plugins/unified_histogram/public/container/services/state_service.ts index 36f50ea8bd36e..01d1a7f0c3f60 100644 --- a/src/plugins/unified_histogram/public/container/services/state_service.ts +++ b/src/plugins/unified_histogram/public/container/services/state_service.ts @@ -6,8 +6,6 @@ * Side Public License, v 1. */ -import type { DataView } from '@kbn/data-views-plugin/common'; -import type { AggregateQuery, Filter, Query, TimeRange } from '@kbn/es-query'; import type { RequestAdapter } from '@kbn/inspector-plugin/common'; import type { Suggestion } from '@kbn/lens-plugin/public'; import { BehaviorSubject, Observable } from 'rxjs'; @@ -30,10 +28,6 @@ export interface UnifiedHistogramState { * The current field used for the breakdown */ breakdownField: string | undefined; - /** - * The current selected columns - */ - columns: string[] | undefined; /** * The current Lens suggestion */ @@ -42,38 +36,14 @@ export interface UnifiedHistogramState { * Whether or not the chart is hidden */ chartHidden: boolean; - /** - * The current data view - */ - dataView: DataView; - /** - * The current filters - */ - filters: Filter[]; /** * The current Lens request adapter */ lensRequestAdapter: RequestAdapter | undefined; - /** - * The current query - */ - query: Query | AggregateQuery; - /** - * The current request adapter used for non-Lens requests - */ - requestAdapter: RequestAdapter | undefined; - /** - * The current search session ID - */ - searchSessionId: string | undefined; /** * The current time interval of the chart */ timeInterval: string; - /** - * The current time range - */ - timeRange: TimeRange; /** * The current top panel height */ @@ -103,7 +73,7 @@ export interface UnifiedHistogramStateOptions { /** * The initial state of the container */ - initialState: Partial & Pick; + initialState?: Partial; } /** @@ -122,10 +92,6 @@ export interface UnifiedHistogramStateService { * Sets current Lens suggestion */ setCurrentSuggestion: (suggestion: Suggestion | undefined) => void; - /** - * Sets columns - */ - setColumns: (columns: string[] | undefined) => void; /** * Sets the current top panel height */ @@ -138,17 +104,6 @@ export interface UnifiedHistogramStateService { * Sets the current time interval */ setTimeInterval: (timeInterval: string) => void; - /** - * Sets the current request parameters - */ - setRequestParams: (requestParams: { - dataView?: DataView; - filters?: Filter[]; - query?: Query | AggregateQuery; - requestAdapter?: RequestAdapter | undefined; - searchSessionId?: string | undefined; - timeRange?: TimeRange; - }) => void; /** * Sets the current Lens request adapter */ @@ -177,18 +132,12 @@ export const createStateService = ( initialBreakdownField = getBreakdownField(services.storage, localStorageKeyPrefix); } - const state$ = new BehaviorSubject({ + const state$ = new BehaviorSubject({ breakdownField: initialBreakdownField, chartHidden: initialChartHidden, - columns: [], - filters: [], currentSuggestion: undefined, lensRequestAdapter: undefined, - query: services.data.query.queryString.getDefaultQuery(), - requestAdapter: undefined, - searchSessionId: undefined, timeInterval: 'auto', - timeRange: services.data.query.timefilter.timefilter.getTimeDefaults(), topPanelHeight: initialTopPanelHeight, totalHitsResult: undefined, totalHitsStatus: UnifiedHistogramFetchStatus.uninitialized, @@ -233,25 +182,10 @@ export const createStateService = ( updateState({ currentSuggestion: suggestion }); }, - setColumns: (columns: string[] | undefined) => { - updateState({ columns }); - }, - setTimeInterval: (timeInterval: string) => { updateState({ timeInterval }); }, - setRequestParams: (requestParams: { - dataView?: DataView; - filters?: Filter[]; - query?: Query | AggregateQuery; - requestAdapter?: RequestAdapter | undefined; - searchSessionId?: string | undefined; - timeRange?: TimeRange; - }) => { - updateState(requestParams); - }, - setLensRequestAdapter: (lensRequestAdapter: RequestAdapter | undefined) => { updateState({ lensRequestAdapter }); }, diff --git a/src/plugins/unified_histogram/public/container/utils/state_selectors.ts b/src/plugins/unified_histogram/public/container/utils/state_selectors.ts index 52980e3918295..d11fb1182cc45 100644 --- a/src/plugins/unified_histogram/public/container/utils/state_selectors.ts +++ b/src/plugins/unified_histogram/public/container/utils/state_selectors.ts @@ -9,15 +9,8 @@ import type { UnifiedHistogramState } from '../services/state_service'; export const breakdownFieldSelector = (state: UnifiedHistogramState) => state.breakdownField; -export const columnsSelector = (state: UnifiedHistogramState) => state.columns; export const chartHiddenSelector = (state: UnifiedHistogramState) => state.chartHidden; -export const dataViewSelector = (state: UnifiedHistogramState) => state.dataView; -export const filtersSelector = (state: UnifiedHistogramState) => state.filters; -export const querySelector = (state: UnifiedHistogramState) => state.query; -export const requestAdapterSelector = (state: UnifiedHistogramState) => state.requestAdapter; -export const searchSessionIdSelector = (state: UnifiedHistogramState) => state.searchSessionId; export const timeIntervalSelector = (state: UnifiedHistogramState) => state.timeInterval; -export const timeRangeSelector = (state: UnifiedHistogramState) => state.timeRange; export const topPanelHeightSelector = (state: UnifiedHistogramState) => state.topPanelHeight; export const totalHitsResultSelector = (state: UnifiedHistogramState) => state.totalHitsResult; export const totalHitsStatusSelector = (state: UnifiedHistogramState) => state.totalHitsStatus; diff --git a/src/plugins/unified_histogram/public/index.ts b/src/plugins/unified_histogram/public/index.ts index b183a5a1f8180..5b32836bfb258 100644 --- a/src/plugins/unified_histogram/public/index.ts +++ b/src/plugins/unified_histogram/public/index.ts @@ -9,11 +9,9 @@ import { UnifiedHistogramPublicPlugin } from './plugin'; export type { - UnifiedHistogramUninitializedApi, - UnifiedHistogramInitializedApi, UnifiedHistogramApi, UnifiedHistogramContainerProps, - UnifiedHistogramInitializeOptions, + UnifiedHistogramCreationOptions, UnifiedHistogramState, UnifiedHistogramStateOptions, } from './container'; diff --git a/src/plugins/unified_histogram/public/layout/layout.tsx b/src/plugins/unified_histogram/public/layout/layout.tsx index 89059a320fcf2..3c5b021e0b08d 100644 --- a/src/plugins/unified_histogram/public/layout/layout.tsx +++ b/src/plugins/unified_histogram/public/layout/layout.tsx @@ -61,6 +61,10 @@ export interface UnifiedHistogramLayoutProps extends PropsWithChildren * The current time range */ timeRange?: TimeRange; + /** + * The relative time range, used when timeRange is an absolute range (e.g. for edit visualization button) + */ + relativeTimeRange?: TimeRange; /** * The current columns */ @@ -113,10 +117,6 @@ export interface UnifiedHistogramLayoutProps extends PropsWithChildren * The Lens suggestions API */ lensSuggestionsApi: LensSuggestionsApi; - /** - * Callback to get the relative time range, useful when passing an absolute time range (e.g. for edit visualization button) - */ - getRelativeTimeRange?: () => TimeRange; /** * Callback to update the topPanelHeight prop when a resize is triggered */ @@ -165,6 +165,7 @@ export const UnifiedHistogramLayout = ({ currentSuggestion: originalSuggestion, isPlainRecord, timeRange, + relativeTimeRange, columns, request, hits, @@ -178,7 +179,6 @@ export const UnifiedHistogramLayout = ({ disabledActions, lensSuggestionsApi, input$, - getRelativeTimeRange, onTopPanelHeightChange, onChartHiddenChange, onTimeIntervalChange, @@ -250,6 +250,7 @@ export const UnifiedHistogramLayout = ({ query={query} filters={filters} timeRange={timeRange} + relativeTimeRange={relativeTimeRange} request={request} hits={hits} currentSuggestion={currentSuggestion} @@ -263,7 +264,6 @@ export const UnifiedHistogramLayout = ({ disableTriggers={disableTriggers} disabledActions={disabledActions} input$={input$} - getRelativeTimeRange={getRelativeTimeRange} onResetChartHeight={onResetChartHeight} onChartHiddenChange={onChartHiddenChange} onTimeIntervalChange={onTimeIntervalChange} diff --git a/src/plugins/unified_histogram/public/mocks.ts b/src/plugins/unified_histogram/public/mocks.ts index 9eb6463d34d1c..544d0ed4cfd6e 100644 --- a/src/plugins/unified_histogram/public/mocks.ts +++ b/src/plugins/unified_histogram/public/mocks.ts @@ -7,26 +7,15 @@ */ import { Observable } from 'rxjs'; -import type { UnifiedHistogramInitializedApi, UnifiedHistogramUninitializedApi } from './container'; +import type { UnifiedHistogramApi } from './container'; -export type MockUnifiedHistogramApi = Omit & - Omit & { initialized: boolean }; - -export const createMockUnifiedHistogramApi = ( - { initialized }: { initialized: boolean } = { initialized: false } -) => { - const api: MockUnifiedHistogramApi = { - initialized, - initialize: jest.fn(() => { - api.initialized = true; - }), +export const createMockUnifiedHistogramApi = () => { + const api: UnifiedHistogramApi = { state$: new Observable(), setChartHidden: jest.fn(), setTopPanelHeight: jest.fn(), setBreakdownField: jest.fn(), - setColumns: jest.fn(), setTimeInterval: jest.fn(), - setRequestParams: jest.fn(), setTotalHits: jest.fn(), refetch: jest.fn(), }; diff --git a/src/plugins/unified_search/public/filter_bar/filter_editor/filter_editor.tsx b/src/plugins/unified_search/public/filter_bar/filter_editor/filter_editor.tsx index 6b97786ea7cf9..70586bdd39857 100644 --- a/src/plugins/unified_search/public/filter_bar/filter_editor/filter_editor.tsx +++ b/src/plugins/unified_search/public/filter_bar/filter_editor/filter_editor.tsx @@ -510,7 +510,7 @@ class FilterEditorComponent extends Component { const alias = customLabel || null; const { $state, - meta: { disabled = false, negate = false }, + meta: { disabled = false }, } = this.props.filter; if (!$state || !$state.store || !selectedDataView) { @@ -533,12 +533,14 @@ class FilterEditorComponent extends Component { }, }; } else { + // for the combined filters created on the builder, negate should always be false, + // the global negation changes only from the exclude/inclue results panel item newFilter = buildCombinedFilter( BooleanRelation.AND, updatedFilters, selectedDataView, disabled, - negate, + false, alias, $state.store ); diff --git a/src/plugins/unified_search/public/search_bar/create_search_bar.tsx b/src/plugins/unified_search/public/search_bar/create_search_bar.tsx index 3224220d12ec1..0590f4c05bfb6 100644 --- a/src/plugins/unified_search/public/search_bar/create_search_bar.tsx +++ b/src/plugins/unified_search/public/search_bar/create_search_bar.tsx @@ -145,6 +145,9 @@ export function createSearchBar({ // Handle queries const onQuerySubmitRef = useRef(props.onQuerySubmit); + useEffect(() => { + onQuerySubmitRef.current = props.onQuerySubmit; + }, [props.onQuerySubmit]); // handle service state updates. // i.e. filters being added from a visualization directly to filterManager. const { filters } = useFilterManager({ diff --git a/test/functional/apps/context/_filters.ts b/test/functional/apps/context/_filters.ts index 6b1c52250be91..b3b8cc0a1fd62 100644 --- a/test/functional/apps/context/_filters.ts +++ b/test/functional/apps/context/_filters.ts @@ -22,8 +22,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const browser = getService('browser'); const PageObjects = getPageObjects(['common', 'context']); + const testSubjects = getService('testSubjects'); - describe('context filters', function contextSize() { + // FLAKY: https://github.com/elastic/kibana/issues/154387 + describe.skip('context filters', function contextSize() { beforeEach(async function () { await PageObjects.context.navigateTo(TEST_INDEX_PATTERN, TEST_ANCHOR_ID, { columns: TEST_COLUMN_NAMES, @@ -227,5 +229,22 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await filterBar.getFilterEditorPreview()).to.equal('extension: is one of png, jpeg'); }); + + it('should display the negated values correctly', async () => { + await filterBar.addFilter({ field: 'extension', operation: 'is not', value: 'png' }); + + await PageObjects.context.waitUntilContextLoadingHasFinished(); + expect(await filterBar.getFilterCount()).to.be(1); + const filterLabel = await filterBar.getFiltersLabel(); + expect(filterLabel[0]).to.be('NOT extension: png'); + + await filterBar.clickEditFilterById('0'); + await filterBar.addAndFilter('0'); + await filterBar.createFilter({ field: 'extension', operation: 'is', value: 'jpeg' }, '0.1'); + await testSubjects.clickWhenNotDisabled('saveFilter'); + + const filterLabelUpdated = await filterBar.getFiltersLabel(); + expect(filterLabelUpdated[0]).to.be('NOT extension: png AND extension: jpeg'); + }); }); } diff --git a/test/functional/page_objects/home_page.ts b/test/functional/page_objects/home_page.ts index a6a5e2e6bc9ea..ac942c70b5d90 100644 --- a/test/functional/page_objects/home_page.ts +++ b/test/functional/page_objects/home_page.ts @@ -57,7 +57,7 @@ export class HomePageObject extends FtrService { } async isGuidedOnboardingLandingDisplayed() { - return await this.testSubjects.isDisplayed('onboarding--landing-page'); + return await this.testSubjects.isDisplayed('guided-onboarding--landing-page'); } async isHomePageDisplayed() { diff --git a/test/functional/services/filter_bar.ts b/test/functional/services/filter_bar.ts index a4e57e5bff2f8..0c44545cae1f4 100644 --- a/test/functional/services/filter_bar.ts +++ b/test/functional/services/filter_bar.ts @@ -208,7 +208,7 @@ export class FilterBarService extends FtrService { await addOrBtn.click(); } - private async addAndFilter(path: string) { + public async addAndFilter(path: string) { const filterForm = await this.testSubjects.find(`filter-${path}`); const addAndBtn = await filterForm.findByTestSubject('add-and-filter'); await addAndBtn.click(); @@ -273,7 +273,7 @@ export class FilterBarService extends FtrService { return 'filters' in filter && 'condition' in filter; } - private async createFilter(filter: Filter, path: string = '0'): Promise { + public async createFilter(filter: Filter, path: string = '0'): Promise { if (this.isFilterNode(filter)) { let startedAdding = false; for (const [index, f] of filter.filters.entries()) { @@ -302,9 +302,12 @@ export class FilterBarService extends FtrService { await this.createFilter(filter); + await this.testSubjects.scrollIntoView('saveFilter'); await this.testSubjects.clickWhenNotDisabled('saveFilter'); }); - await this.testSubjects.waitForDeleted('saveFilter'); + await this.retry.try(async () => { + await this.testSubjects.waitForDeleted('saveFilter'); + }); await this.header.awaitGlobalLoadingIndicatorHidden(); } diff --git a/x-pack/packages/observability/alert_details/index.ts b/x-pack/packages/observability/alert_details/index.ts index 0ee2aab37d7dc..794dd06922c7c 100644 --- a/x-pack/packages/observability/alert_details/index.ts +++ b/x-pack/packages/observability/alert_details/index.ts @@ -6,4 +6,5 @@ */ export { AlertAnnotation } from './src/components/alert_annotation'; -export { getAlertTimeRange } from './src/helpers/get_alert_time_range'; +export { AlertActiveTimeRangeAnnotation } from './src/components/alert_active_time_range_annotation'; +export { getPaddedAlertTimeRange } from './src/helpers/get_padded_alert_time_range'; diff --git a/x-pack/packages/observability/alert_details/src/components/alert_active_time_range_annotation.tsx b/x-pack/packages/observability/alert_details/src/components/alert_active_time_range_annotation.tsx new file mode 100644 index 0000000000000..9f1beb3c77669 --- /dev/null +++ b/x-pack/packages/observability/alert_details/src/components/alert_active_time_range_annotation.tsx @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { RectAnnotation } from '@elastic/charts'; +import { i18n } from '@kbn/i18n'; + +interface Props { + alertStart: number; + alertEnd?: number; + color: string; + id: string; +} + +const RECT_ANNOTATION_TITLE = i18n.translate( + 'observabilityAlertDetails.alertActiveTimeRangeAnnotation.detailsTooltip', + { + defaultMessage: 'Active', + } +); + +export function AlertActiveTimeRangeAnnotation({ alertStart, alertEnd, color, id }: Props) { + return ( + + ); +} diff --git a/x-pack/packages/observability/alert_details/src/components/alert_annotation.tsx b/x-pack/packages/observability/alert_details/src/components/alert_annotation.tsx index 1f435ea705153..4579bc6976b9c 100644 --- a/x-pack/packages/observability/alert_details/src/components/alert_annotation.tsx +++ b/x-pack/packages/observability/alert_details/src/components/alert_annotation.tsx @@ -18,9 +18,12 @@ interface Props { id: string; } -const ANNOTATION_TITLE = i18n.translate('observabilityAlertDetails.alertAnnotation.title', { - defaultMessage: 'Alert started', -}); +const ANNOTATION_TITLE = i18n.translate( + 'observabilityAlertDetails.alertAnnotation.detailsTooltip', + { + defaultMessage: 'Alert started', + } +); export function AlertAnnotation({ alertStart, color, dateFormat, id }: Props) { return ( diff --git a/x-pack/packages/observability/alert_details/src/helpers/get_alert_time_range.test.ts b/x-pack/packages/observability/alert_details/src/helpers/get_padded_alert_time_range.test.ts similarity index 85% rename from x-pack/packages/observability/alert_details/src/helpers/get_alert_time_range.test.ts rename to x-pack/packages/observability/alert_details/src/helpers/get_padded_alert_time_range.test.ts index fcf3aba760bd4..cd05650db7a56 100644 --- a/x-pack/packages/observability/alert_details/src/helpers/get_alert_time_range.test.ts +++ b/x-pack/packages/observability/alert_details/src/helpers/get_padded_alert_time_range.test.ts @@ -5,9 +5,9 @@ * 2.0. */ -import { getAlertTimeRange } from './get_alert_time_range'; +import { getPaddedAlertTimeRange } from './get_padded_alert_time_range'; -describe('getAlertTimeRange', () => { +describe('getPaddedAlertTimeRange', () => { const mockedDate = '2023-03-28T09:22:32.660Z'; const mockDate = jest .spyOn(global.Date, 'now') @@ -31,7 +31,7 @@ describe('getAlertTimeRange', () => { ]; it.each(testData)('%s', (_, start, end, output) => { - expect(getAlertTimeRange(start, end)).toEqual(output); + expect(getPaddedAlertTimeRange(start, end)).toEqual(output); }); describe('active alert', () => { @@ -43,7 +43,7 @@ describe('getAlertTimeRange', () => { from: '2023-03-28T03:45:02.660Z', to: mockedDate, }; - expect(getAlertTimeRange(start)).toEqual(output); + expect(getPaddedAlertTimeRange(start)).toEqual(output); }); it('with end time than 10 minutes before now', () => { @@ -55,7 +55,7 @@ describe('getAlertTimeRange', () => { from: '2023-03-28T04:47:32.660Z', to: mockedDate, }; - expect(getAlertTimeRange(start, end)).toEqual(output); + expect(getPaddedAlertTimeRange(start, end)).toEqual(output); }); }); }); diff --git a/x-pack/packages/observability/alert_details/src/helpers/get_alert_time_range.ts b/x-pack/packages/observability/alert_details/src/helpers/get_padded_alert_time_range.ts similarity index 90% rename from x-pack/packages/observability/alert_details/src/helpers/get_alert_time_range.ts rename to x-pack/packages/observability/alert_details/src/helpers/get_padded_alert_time_range.ts index 9d4ea0e04fac1..9c7fc7ec9ecee 100644 --- a/x-pack/packages/observability/alert_details/src/helpers/get_alert_time_range.ts +++ b/x-pack/packages/observability/alert_details/src/helpers/get_padded_alert_time_range.ts @@ -13,7 +13,7 @@ export interface TimeRange { interval?: string; } -export const getAlertTimeRange = (alertStart: string, alertEnd?: string): TimeRange => { +export const getPaddedAlertTimeRange = (alertStart: string, alertEnd?: string): TimeRange => { const alertDuration = moment.duration(moment(alertEnd).diff(moment(alertStart))); const now = moment().toISOString(); const durationMs = diff --git a/x-pack/plugins/alerting/server/lib/is_rule_snoozed.test.ts b/x-pack/plugins/alerting/server/lib/is_rule_snoozed.test.ts index 21fe92a72a3dd..35ece5bd95c41 100644 --- a/x-pack/plugins/alerting/server/lib/is_rule_snoozed.test.ts +++ b/x-pack/plugins/alerting/server/lib/is_rule_snoozed.test.ts @@ -299,7 +299,7 @@ describe('isRuleSnoozed', () => { }, ]; - expect(isRuleSnoozed({ snoozeSchedule: snoozeScheduleA, muteAll: false })).toBe(false); + expect(isRuleSnoozed({ snoozeSchedule: snoozeScheduleA, muteAll: false })).toBe(true); const snoozeScheduleB = [ { duration: 60 * 1000, diff --git a/x-pack/plugins/alerting/server/lib/snooze/index.ts b/x-pack/plugins/alerting/server/lib/snooze/index.ts index 6c04be20ca97c..beb38a3f9650b 100644 --- a/x-pack/plugins/alerting/server/lib/snooze/index.ts +++ b/x-pack/plugins/alerting/server/lib/snooze/index.ts @@ -7,3 +7,4 @@ export { isSnoozeActive } from './is_snooze_active'; export { isSnoozeExpired } from './is_snooze_expired'; +export { utcToLocalUtc, localUtcToUtc } from './timezone_helpers'; diff --git a/x-pack/plugins/alerting/server/lib/snooze/is_snooze_active.test.ts b/x-pack/plugins/alerting/server/lib/snooze/is_snooze_active.test.ts new file mode 100644 index 0000000000000..d42824d6e840a --- /dev/null +++ b/x-pack/plugins/alerting/server/lib/snooze/is_snooze_active.test.ts @@ -0,0 +1,233 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import moment from 'moment'; +import { RRule } from 'rrule'; +import sinon from 'sinon'; +import { RRuleRecord } from '../../types'; +import { isSnoozeActive } from './is_snooze_active'; + +let fakeTimer: sinon.SinonFakeTimers; + +describe('isSnoozeExpired', () => { + afterAll(() => fakeTimer.restore()); + + test('snooze is NOT active byweekday', () => { + // Set the current time as: + // - Feb 27 2023 08:15:00 GMT+0000 - Monday + fakeTimer = sinon.useFakeTimers(new Date('2023-02-27T06:15:00.000Z')); + + // Try to get snooze end time with: + // - Start date of: Feb 24 2023 23:00:00 GMT+0000 - Friday + // - End date of: Feb 27 2023 06:00:00 GMT+0000 - Monday + // - Which is obtained from start date + 2 days and 7 hours (198000000 ms) + const snoozeA = { + duration: 198000000, + rRule: { + byweekday: ['SA'], + tzid: 'Europe/Madrid', + freq: RRule.DAILY, + interval: 1, + dtstart: '2023-02-24T23:00:00.000Z', + } as RRuleRecord, + id: '9141dc1f-ed85-4656-91e4-119173105432', + }; + expect(isSnoozeActive(snoozeA)).toMatchInlineSnapshot(`null`); + fakeTimer.restore(); + }); + + test('snooze is active byweekday', () => { + // Set the current time as: + // - Feb 25 2023 08:15:00 GMT+0000 - Saturday + fakeTimer = sinon.useFakeTimers(new Date('2023-02-25T08:15:00.000Z')); + + // Try to get snooze end time with: + // - Start date of: Feb 24 2023 23:00:00 GMT+0000 - Friday + // - End date of: Feb 27 2023 06:00:00 GMT+0000 - Monday + // - Which is obtained from start date + 2 days and 7 hours (198000000 ms) + const snoozeA = { + duration: 198000000, + rRule: { + byweekday: ['SA'], + tzid: 'Europe/Madrid', + freq: RRule.DAILY, + interval: 1, + dtstart: '2023-02-24T23:00:00.000Z', + } as RRuleRecord, + id: '9141dc1f-ed85-4656-91e4-119173105432', + }; + expect(isSnoozeActive(snoozeA)).toMatchInlineSnapshot(` + Object { + "id": "9141dc1f-ed85-4656-91e4-119173105432", + "lastOccurrence": 2023-02-24T23:00:00.000Z, + "snoozeEndTime": 2023-02-27T06:00:00.000Z, + } + `); + fakeTimer.restore(); + }); + + test('snooze is NOT active in recurrence byweekday', () => { + // Set the current time as: + // - March 01 2023 08:15:00 GMT+0000 - Wednesday + fakeTimer = sinon.useFakeTimers(new Date('2023-03-01T08:15:00.000Z')); + + // Try to get snooze end time with: + // - Start date of: Feb 24 2023 23:00:00 GMT+0000 - Friday + // - End date of: Feb 27 2023 06:00:00 GMT+0000 - Monday + // - Which is obtained from start date + 2 days and 7 hours (198000000 ms) + const snoozeA = { + duration: 198000000, + rRule: { + byweekday: ['SA'], + tzid: 'Europe/Madrid', + freq: RRule.DAILY, + interval: 1, + dtstart: '2023-02-24T23:00:00.000Z', + } as RRuleRecord, + id: '9141dc1f-ed85-4656-91e4-119173105432', + }; + expect(isSnoozeActive(snoozeA)).toMatchInlineSnapshot(`null`); + fakeTimer.restore(); + }); + + test('snooze is active in recurrence byweekday', () => { + // Set the current time as: + // - March 04 2023 08:15:00 GMT+0000 - Saturday + fakeTimer = sinon.useFakeTimers(new Date('2023-03-04T08:15:00.000Z')); + + // Try to get snooze end time with: + // - Start date of: Feb 24 2023 23:00:00 GMT+0000 - Friday + // - End date of: Feb 27 2023 06:00:00 GMT+0000 - Monday + // - Which is obtained from start date + 2 days and 7 hours (198000000 ms) + const snoozeA = { + duration: 198000000, + rRule: { + byweekday: ['SA'], + tzid: 'Europe/Madrid', + freq: RRule.DAILY, + interval: 1, + dtstart: '2023-02-24T23:00:00.000Z', + } as RRuleRecord, + id: '9141dc1f-ed85-4656-91e4-119173105432', + }; + expect(isSnoozeActive(snoozeA)).toMatchInlineSnapshot(` + Object { + "id": "9141dc1f-ed85-4656-91e4-119173105432", + "lastOccurrence": 2023-03-04T00:00:00.000Z, + "snoozeEndTime": 2023-03-06T06:00:00.000Z, + } + `); + fakeTimer.restore(); + }); + + test('snooze is NOT active bymonth', () => { + // Set the current time as: + // - Feb 27 2023 08:15:00 GMT+0000 - Monday + fakeTimer = sinon.useFakeTimers(new Date('2023-02-09T08:15:00.000Z')); + + // Try to get snooze end time with: + // - Start date of: Jan 01 2023 00:00:00 GMT+0000 - Sunday + // - End date of: Jan 31 2023 06:00:00 GMT+0000 - Tuesday + // - Which is obtained from start date + 1 month (2629800000 ms) + const snoozeA = { + duration: moment('2023-01', 'YYYY-MM').daysInMonth() * 24 * 60 * 60 * 1000, // 1 month + rRule: { + freq: 0, + interval: 1, + bymonthday: [1], + bymonth: [1], + tzid: 'Europe/Madrid', + dtstart: '2023-01-01T00:00:00.000Z', + } as RRuleRecord, + id: '9141dc1f-ed85-4656-91e4-119173105432', + }; + expect(isSnoozeActive(snoozeA)).toMatchInlineSnapshot(`null`); + fakeTimer.restore(); + }); + + test('snooze is active bymonth', () => { + // Set the current time as: + // - Jan 25 2023 08:15:00 GMT+0000 - Saturday + fakeTimer = sinon.useFakeTimers(new Date('2023-01-25T08:15:00.000Z')); + + // Try to get snooze end time with: + // - Start date of: Jan 01 2023 00:00:00 GMT+0000 - Sunday + // - End date of: Jan 31 2023 06:00:00 GMT+0000 - Tuesday + // - Which is obtained from start date + 1 month (2629800000 ms) + const snoozeA = { + duration: moment('2023-01', 'YYYY-MM').daysInMonth() * 24 * 60 * 60 * 1000, + rRule: { + bymonthday: [1], + bymonth: [1], + tzid: 'Europe/Madrid', + freq: RRule.MONTHLY, + interval: 1, + dtstart: '2023-01-01T00:00:00.000Z', + } as RRuleRecord, + id: '9141dc1f-ed85-4656-91e4-119173105432', + }; + expect(isSnoozeActive(snoozeA)).toMatchInlineSnapshot(` + Object { + "id": "9141dc1f-ed85-4656-91e4-119173105432", + "lastOccurrence": 2023-01-01T00:00:00.000Z, + "snoozeEndTime": 2023-02-01T00:00:00.000Z, + } + `); + fakeTimer.restore(); + }); + + test('snooze is NOT active bymonth after the first month', () => { + // Set the current time as: + // - Feb 01 2023 00:00:00 GMT+0000 - Wednesday + fakeTimer = sinon.useFakeTimers(new Date('2023-02-01T00:00:00.000Z')); + + // Try to get snooze end time with: + // - Start date of: Jan 01 2023 00:00:00 GMT+0000 - Sunday + // - End date of: Jan 31 2023 06:00:00 GMT+0000 - Tuesday + // - Which is obtained from start date + 1 month (2629800000 ms) + const snoozeA = { + duration: moment('2023-01', 'YYYY-MM').daysInMonth() * 24 * 60 * 60 * 1000, + rRule: { + bymonthday: [1], + bymonth: [1], + tzid: 'Europe/Madrid', + freq: RRule.MONTHLY, + interval: 1, + dtstart: '2023-01-01T00:00:00.000Z', + } as RRuleRecord, + id: '9141dc1f-ed85-4656-91e4-119173105432', + }; + expect(isSnoozeActive(snoozeA)).toMatchInlineSnapshot(`null`); + fakeTimer.restore(); + }); + + // THIS is wrong, we need to do the same thing that we did for `byweekday` for + test('snooze is NOT active bymonth before the first month', () => { + // Set the current time as: + // - Dec 31 2022 23:00:00 GMT+0000 - Wednesday + fakeTimer = sinon.useFakeTimers(new Date('2022-12-31T21:00:00.000Z')); + + // Try to get snooze end time with: + // - Start date of: Jan 01 2023 00:00:00 GMT+0000 - Sunday + // - End date of: Jan 31 2023 06:00:00 GMT+0000 - Tuesday + // - Which is obtained from start date + 1 month (2629800000 ms) + const snoozeA = { + duration: moment('2023-01', 'YYYY-MM').daysInMonth() * 24 * 60 * 60 * 1000, + rRule: { + bymonthday: [1], + bymonth: [1], + tzid: 'Europe/Madrid', + freq: RRule.MONTHLY, + interval: 1, + dtstart: '2023-01-01T00:00:00.000Z', + } as RRuleRecord, + id: '9141dc1f-ed85-4656-91e4-119173105432', + }; + expect(isSnoozeActive(snoozeA)).toMatchInlineSnapshot(`null`); + fakeTimer.restore(); + }); +}); diff --git a/x-pack/plugins/alerting/server/lib/snooze/is_snooze_active.ts b/x-pack/plugins/alerting/server/lib/snooze/is_snooze_active.ts index e365c75df1bd0..e7081681cabff 100644 --- a/x-pack/plugins/alerting/server/lib/snooze/is_snooze_active.ts +++ b/x-pack/plugins/alerting/server/lib/snooze/is_snooze_active.ts @@ -7,6 +7,7 @@ import { RRule, ByWeekday, Weekday, rrulestr } from 'rrule'; import { RuleSnoozeSchedule } from '../../types'; +import { utcToLocalUtc, localUtcToUtc } from './timezone_helpers'; const MAX_TIMESTAMP = 8640000000000000; @@ -30,23 +31,32 @@ export function isSnoozeActive(snooze: RuleSnoozeSchedule) { }; // Check to see if now is during a recurrence of the snooze + + const { tzid, ...restRRule } = rRule; + const startDate = utcToLocalUtc(new Date(rRule.dtstart), tzid); + const nowDate = utcToLocalUtc(new Date(now), tzid); + try { const rRuleOptions = { - ...rRule, - dtstart: new Date(rRule.dtstart), - until: rRule.until ? new Date(rRule.until) : null, + ...restRRule, + dtstart: startDate, + until: rRule.until ? utcToLocalUtc(new Date(rRule.until), tzid) : null, wkst: rRule.wkst ? Weekday.fromStr(rRule.wkst) : null, byweekday: rRule.byweekday ? parseByWeekday(rRule.byweekday) : null, }; const recurrenceRule = new RRule(rRuleOptions); - const lastOccurrence = recurrenceRule.before(new Date(now), true); + const lastOccurrence = recurrenceRule.before(nowDate, true); if (!lastOccurrence) return null; // Check if the current recurrence has been skipped manually if (snooze.skipRecurrences?.includes(lastOccurrence.toISOString())) return null; const lastOccurrenceEndTime = lastOccurrence.getTime() + duration; - if (now < lastOccurrenceEndTime) - return { lastOccurrence, snoozeEndTime: new Date(lastOccurrenceEndTime), id }; + if (nowDate.getTime() < lastOccurrenceEndTime) + return { + lastOccurrence, + snoozeEndTime: localUtcToUtc(new Date(lastOccurrenceEndTime), tzid), + id, + }; } catch (e) { throw new Error(`Failed to process RRule ${rRule}: ${e}`); } diff --git a/x-pack/plugins/alerting/server/lib/snooze/timezone_helpers.ts b/x-pack/plugins/alerting/server/lib/snooze/timezone_helpers.ts new file mode 100644 index 0000000000000..e32e8623103bd --- /dev/null +++ b/x-pack/plugins/alerting/server/lib/snooze/timezone_helpers.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import moment from 'moment-timezone'; + +/** + * Converts the UTC date into the user's local time zone, but still in UTC. + * This must be done because rrule does not care about timezones, so for the result + * to be correct, we must ensure everything is timezone agnostic. + * + * example: 2023-03-29 08:00:00 CET -> 2023-03-29 08:00:00 UTC + */ +export const utcToLocalUtc = (date: Date, tz: string) => { + const localTime = moment(date).tz(tz); + const localTimeInUTC = moment(localTime).tz('UTC', true); + return localTimeInUTC.utc().toDate(); +}; + +/** + * Converts the local date in UTC back into actual UTC. After rrule does its thing, + * we would still like to keep everything in UTC in the business logic, hence why we + * need to convert everything back + * + * Example: 2023-03-29 08:00:00 UTC (from the utcToLocalUtc output) -> 2023-03-29 06:00:00 UTC (Real UTC) + */ +export const localUtcToUtc = (date: Date, tz: string) => { + const localTimeString = moment.utc(date).format('YYYY-MM-DD HH:mm:ss.SSS'); + return moment.tz(localTimeString, tz).utc().toDate(); +}; diff --git a/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap b/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap index 8a9559fc4f7b9..55bbf9d8b5fc1 100644 --- a/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap +++ b/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap @@ -76,6 +76,9 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "opentelemetry/ruby": { "type": "long" }, + "opentelemetry/rust": { + "type": "long" + }, "opentelemetry/swift": { "type": "long" }, diff --git a/x-pack/plugins/apm/common/agent_name.ts b/x-pack/plugins/apm/common/agent_name.ts index 4accb55055a55..21cecfcf348f7 100644 --- a/x-pack/plugins/apm/common/agent_name.ts +++ b/x-pack/plugins/apm/common/agent_name.ts @@ -27,6 +27,7 @@ export const OPEN_TELEMETRY_AGENT_NAMES: AgentName[] = [ 'opentelemetry/php', 'opentelemetry/python', 'opentelemetry/ruby', + 'opentelemetry/rust', 'opentelemetry/swift', 'opentelemetry/webjs', ]; diff --git a/x-pack/plugins/apm/public/components/shared/agent_icon/get_agent_icon.test.ts b/x-pack/plugins/apm/public/components/shared/agent_icon/get_agent_icon.test.ts index 66dace81b8eed..aac5fc19ca37b 100644 --- a/x-pack/plugins/apm/public/components/shared/agent_icon/get_agent_icon.test.ts +++ b/x-pack/plugins/apm/public/components/shared/agent_icon/get_agent_icon.test.ts @@ -14,7 +14,7 @@ const examples = { go: 'go', nodejs: 'nodejs', ocaml: 'ocaml', - 'opentelemetry/cpp': 'opentelemetry', + 'opentelemetry/cpp': 'cpp', 'opentelemetry/dotnet': 'dotnet', 'opentelemetry/erlang': 'erlang', 'opentelemetry/go': 'go', @@ -22,6 +22,7 @@ const examples = { 'opentelemetry/php': 'php', 'opentelemetry/python': 'python', 'opentelemetry/ruby': 'ruby', + 'opentelemetry/rust': 'rust', otlp: 'opentelemetry', php: 'php', python: 'python', diff --git a/x-pack/plugins/apm/public/components/shared/agent_icon/get_agent_icon.ts b/x-pack/plugins/apm/public/components/shared/agent_icon/get_agent_icon.ts index 1b5b782d727d0..9a775df78c4e3 100644 --- a/x-pack/plugins/apm/public/components/shared/agent_icon/get_agent_icon.ts +++ b/x-pack/plugins/apm/public/components/shared/agent_icon/get_agent_icon.ts @@ -14,8 +14,11 @@ import { } from '../../../../common/agent_name'; import { AgentName } from '../../../../typings/es_schemas/ui/fields/agent'; import defaultIcon from '../span_icon/icons/default.svg'; +import cppIcon from './icons/cpp.svg'; +import darkCppIcon from './icons/cpp_dark.svg'; import dotNetIcon from './icons/dot_net.svg'; import erlangIcon from './icons/erlang.svg'; +import darkErlangIcon from './icons/erlang_dark.svg'; import goIcon from './icons/go.svg'; import iosIcon from './icons/ios.svg'; import darkIosIcon from './icons/ios_dark.svg'; @@ -34,6 +37,7 @@ import darkRustIcon from './icons/rust_dark.svg'; import androidIcon from './icons/android.svg'; const agentIcons: { [key: string]: string } = { + cpp: cppIcon, dotnet: dotNetIcon, erlang: erlangIcon, go: goIcon, @@ -52,6 +56,8 @@ const agentIcons: { [key: string]: string } = { const darkAgentIcons: { [key: string]: string } = { ...agentIcons, + cpp: darkCppIcon, + erlang: darkErlangIcon, ios: darkIosIcon, php: darkPhpIcon, rum: darkRumJsIcon, diff --git a/x-pack/plugins/apm/public/components/shared/agent_icon/icons/cpp.svg b/x-pack/plugins/apm/public/components/shared/agent_icon/icons/cpp.svg new file mode 100644 index 0000000000000..57d5109d7c8d5 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/agent_icon/icons/cpp.svg @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/x-pack/plugins/apm/public/components/shared/agent_icon/icons/cpp_dark.svg b/x-pack/plugins/apm/public/components/shared/agent_icon/icons/cpp_dark.svg new file mode 100644 index 0000000000000..750e000bc60c7 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/agent_icon/icons/cpp_dark.svg @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/x-pack/plugins/apm/public/components/shared/agent_icon/icons/erlang_dark.svg b/x-pack/plugins/apm/public/components/shared/agent_icon/icons/erlang_dark.svg new file mode 100644 index 0000000000000..49e890157fd83 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/agent_icon/icons/erlang_dark.svg @@ -0,0 +1 @@ + diff --git a/x-pack/plugins/apm/server/lib/apm_telemetry/schema.ts b/x-pack/plugins/apm/server/lib/apm_telemetry/schema.ts index 5156345dbafeb..3dacba9c68a07 100644 --- a/x-pack/plugins/apm/server/lib/apm_telemetry/schema.ts +++ b/x-pack/plugins/apm/server/lib/apm_telemetry/schema.ts @@ -99,6 +99,7 @@ const apmPerAgentSchema: Pick< 'opentelemetry/php': long, 'opentelemetry/python': long, 'opentelemetry/ruby': long, + 'opentelemetry/rust': long, 'opentelemetry/swift': long, 'opentelemetry/webjs': long, }, diff --git a/x-pack/plugins/apm/server/routes/agent_explorer/get_agent_url_repository.ts b/x-pack/plugins/apm/server/routes/agent_explorer/get_agent_url_repository.ts index 2ec8d5b66c4c5..64b18eff090c4 100644 --- a/x-pack/plugins/apm/server/routes/agent_explorer/get_agent_url_repository.ts +++ b/x-pack/plugins/apm/server/routes/agent_explorer/get_agent_url_repository.ts @@ -28,6 +28,7 @@ const agentsDocPageName: Partial> = { 'opentelemetry/php': 'php', 'opentelemetry/python': 'python', 'opentelemetry/ruby': 'ruby', + 'opentelemetry/rust': 'rust', 'opentelemetry/swift': 'swift', 'opentelemetry/webjs': 'js', }; diff --git a/x-pack/plugins/apm/typings/es_schemas/ui/fields/agent.ts b/x-pack/plugins/apm/typings/es_schemas/ui/fields/agent.ts index 2e29ba07d327f..4f19793004815 100644 --- a/x-pack/plugins/apm/typings/es_schemas/ui/fields/agent.ts +++ b/x-pack/plugins/apm/typings/es_schemas/ui/fields/agent.ts @@ -29,6 +29,7 @@ export type OpenTelemetryAgentName = | 'opentelemetry/php' | 'opentelemetry/python' | 'opentelemetry/ruby' + | 'opentelemetry/rust' | 'opentelemetry/swift' | 'opentelemetry/webjs'; diff --git a/x-pack/plugins/asset_manager/docs/index.md b/x-pack/plugins/asset_manager/docs/index.md index a6c3be63fba2d..8ed01ac575c9f 100644 --- a/x-pack/plugins/asset_manager/docs/index.md +++ b/x-pack/plugins/asset_manager/docs/index.md @@ -406,6 +406,638 @@ GET /assets?from=2023-03-25T17:44:44.000Z&to=2023-03-25T18:44:44.000Z&ean=k8s.no +### GET /assets/diff + +Returns assets found in the two time ranges, split by what occurs in only either or in both. + +#### Request + +| Option | Type | Required? | Default | Description | +| :--- | :--- | :--- | :--- | :--- | +| aFrom | RangeDate | Yes | N/A | Starting point for baseline date range to search for assets within | +| aTo | RangeDate | Yes | N/A | End point for baseline date range to search for assets within | +| bFrom | RangeDate | Yes | N/A | Starting point for comparison date range | +| bTo | RangeDate | Yes | N/A | End point for comparison date range | +| type | AssetType[] | No | all | Restrict results to one or more asset.type value | + +#### Responses + +
+ +Request where comparison range is missing assets that are found in the baseline range + +```curl +GET /assets/diff?aFrom=2022-02-07T00:00:00.000Z&aTo=2022-02-07T01:30:00.000Z&bFrom=2022-02-07T01:00:00.000Z&bTo=2022-02-07T02:00:00.000Z + +{ + "onlyInA": [ + { + "@timestamp": "2022-02-07T00:00:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200xrg1", + "asset.name": "k8s-pod-200xrg1-aws", + "asset.ean": "k8s.pod:pod-200xrg1", + "asset.parents": [ + "k8s.node:node-101" + ] + }, + { + "@timestamp": "2022-02-07T00:00:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200dfp2", + "asset.name": "k8s-pod-200dfp2-aws", + "asset.ean": "k8s.pod:pod-200dfp2", + "asset.parents": [ + "k8s.node:node-101" + ] + } + ], + "onlyInB": [], + "inBoth": [ + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.cluster", + "asset.id": "cluster-001", + "asset.name": "Cluster 001 (AWS EKS)", + "asset.ean": "k8s.cluster:cluster-001", + "orchestrator.type": "kubernetes", + "orchestrator.cluster.name": "Cluster 001 (AWS EKS)", + "orchestrator.cluster.id": "cluster-001", + "cloud.provider": "aws", + "cloud.region": "us-east-1", + "cloud.service.name": "eks" + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.cluster", + "asset.id": "cluster-002", + "asset.name": "Cluster 002 (Azure AKS)", + "asset.ean": "k8s.cluster:cluster-002", + "orchestrator.type": "kubernetes", + "orchestrator.cluster.name": "Cluster 002 (Azure AKS)", + "orchestrator.cluster.id": "cluster-002", + "cloud.provider": "azure", + "cloud.region": "eu-west", + "cloud.service.name": "aks" + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.node", + "asset.id": "node-101", + "asset.name": "k8s-node-101-aws", + "asset.ean": "k8s.node:node-101", + "asset.parents": [ + "k8s.cluster:cluster-001" + ], + "orchestrator.type": "kubernetes", + "orchestrator.cluster.name": "Cluster 001 (AWS EKS)", + "orchestrator.cluster.id": "cluster-001", + "cloud.provider": "aws", + "cloud.region": "us-east-1", + "cloud.service.name": "eks" + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.node", + "asset.id": "node-102", + "asset.name": "k8s-node-102-aws", + "asset.ean": "k8s.node:node-102", + "asset.parents": [ + "k8s.cluster:cluster-001" + ], + "orchestrator.type": "kubernetes", + "orchestrator.cluster.name": "Cluster 001 (AWS EKS)", + "orchestrator.cluster.id": "cluster-001", + "cloud.provider": "aws", + "cloud.region": "us-east-1", + "cloud.service.name": "eks" + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.node", + "asset.id": "node-103", + "asset.name": "k8s-node-103-aws", + "asset.ean": "k8s.node:node-103", + "asset.parents": [ + "k8s.cluster:cluster-001" + ], + "orchestrator.type": "kubernetes", + "orchestrator.cluster.name": "Cluster 001 (AWS EKS)", + "orchestrator.cluster.id": "cluster-001", + "cloud.provider": "aws", + "cloud.region": "us-east-1", + "cloud.service.name": "eks" + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200ohr5", + "asset.name": "k8s-pod-200ohr5-aws", + "asset.ean": "k8s.pod:pod-200ohr5", + "asset.parents": [ + "k8s.node:node-102" + ] + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200yyx6", + "asset.name": "k8s-pod-200yyx6-aws", + "asset.ean": "k8s.pod:pod-200yyx6", + "asset.parents": [ + "k8s.node:node-103" + ] + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200psd7", + "asset.name": "k8s-pod-200psd7-aws", + "asset.ean": "k8s.pod:pod-200psd7", + "asset.parents": [ + "k8s.node:node-103" + ] + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200wmc8", + "asset.name": "k8s-pod-200wmc8-aws", + "asset.ean": "k8s.pod:pod-200wmc8", + "asset.parents": [ + "k8s.node:node-103" + ] + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200ugg9", + "asset.name": "k8s-pod-200ugg9-aws", + "asset.ean": "k8s.pod:pod-200ugg9", + "asset.parents": [ + "k8s.node:node-103" + ] + } + ] +} +``` + +
+ +
+ +Request where baseline range is missing assets that are found in the comparison range + +```curl +GET /assets/diff?aFrom=2022-02-07T01:00:00.000Z&aTo=2022-02-07T01:30:00.000Z&bFrom=2022-02-07T01:00:00.000Z&bTo=2022-02-07T03:00:00.000Z + +{ + "onlyInA": [], + "onlyInB": [ + { + "@timestamp": "2022-02-07T03:00:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200wwc3", + "asset.name": "k8s-pod-200wwc3-aws", + "asset.ean": "k8s.pod:pod-200wwc3", + "asset.parents": [ + "k8s.node:node-101" + ] + }, + { + "@timestamp": "2022-02-07T03:00:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200naq4", + "asset.name": "k8s-pod-200naq4-aws", + "asset.ean": "k8s.pod:pod-200naq4", + "asset.parents": [ + "k8s.node:node-102" + ] + } + ], + "inBoth": [ + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.cluster", + "asset.id": "cluster-001", + "asset.name": "Cluster 001 (AWS EKS)", + "asset.ean": "k8s.cluster:cluster-001", + "orchestrator.type": "kubernetes", + "orchestrator.cluster.name": "Cluster 001 (AWS EKS)", + "orchestrator.cluster.id": "cluster-001", + "cloud.provider": "aws", + "cloud.region": "us-east-1", + "cloud.service.name": "eks" + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.cluster", + "asset.id": "cluster-002", + "asset.name": "Cluster 002 (Azure AKS)", + "asset.ean": "k8s.cluster:cluster-002", + "orchestrator.type": "kubernetes", + "orchestrator.cluster.name": "Cluster 002 (Azure AKS)", + "orchestrator.cluster.id": "cluster-002", + "cloud.provider": "azure", + "cloud.region": "eu-west", + "cloud.service.name": "aks" + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.node", + "asset.id": "node-101", + "asset.name": "k8s-node-101-aws", + "asset.ean": "k8s.node:node-101", + "asset.parents": [ + "k8s.cluster:cluster-001" + ], + "orchestrator.type": "kubernetes", + "orchestrator.cluster.name": "Cluster 001 (AWS EKS)", + "orchestrator.cluster.id": "cluster-001", + "cloud.provider": "aws", + "cloud.region": "us-east-1", + "cloud.service.name": "eks" + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.node", + "asset.id": "node-102", + "asset.name": "k8s-node-102-aws", + "asset.ean": "k8s.node:node-102", + "asset.parents": [ + "k8s.cluster:cluster-001" + ], + "orchestrator.type": "kubernetes", + "orchestrator.cluster.name": "Cluster 001 (AWS EKS)", + "orchestrator.cluster.id": "cluster-001", + "cloud.provider": "aws", + "cloud.region": "us-east-1", + "cloud.service.name": "eks" + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.node", + "asset.id": "node-103", + "asset.name": "k8s-node-103-aws", + "asset.ean": "k8s.node:node-103", + "asset.parents": [ + "k8s.cluster:cluster-001" + ], + "orchestrator.type": "kubernetes", + "orchestrator.cluster.name": "Cluster 001 (AWS EKS)", + "orchestrator.cluster.id": "cluster-001", + "cloud.provider": "aws", + "cloud.region": "us-east-1", + "cloud.service.name": "eks" + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200ohr5", + "asset.name": "k8s-pod-200ohr5-aws", + "asset.ean": "k8s.pod:pod-200ohr5", + "asset.parents": [ + "k8s.node:node-102" + ] + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200yyx6", + "asset.name": "k8s-pod-200yyx6-aws", + "asset.ean": "k8s.pod:pod-200yyx6", + "asset.parents": [ + "k8s.node:node-103" + ] + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200psd7", + "asset.name": "k8s-pod-200psd7-aws", + "asset.ean": "k8s.pod:pod-200psd7", + "asset.parents": [ + "k8s.node:node-103" + ] + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200wmc8", + "asset.name": "k8s-pod-200wmc8-aws", + "asset.ean": "k8s.pod:pod-200wmc8", + "asset.parents": [ + "k8s.node:node-103" + ] + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200ugg9", + "asset.name": "k8s-pod-200ugg9-aws", + "asset.ean": "k8s.pod:pod-200ugg9", + "asset.parents": [ + "k8s.node:node-103" + ] + } + ] +} +``` + +
+ +
+ +Request where each range is missing assets found in the other range + +```curl +GET /assets/diff?aFrom=2022-02-07T00:00:00.000Z&aTo=2022-02-07T01:30:00.000Z&bFrom=2022-02-07T01:00:00.000Z&bTo=2022-02-07T03:00:00.000Z + +{ + "onlyInA": [ + { + "@timestamp": "2022-02-07T00:00:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200xrg1", + "asset.name": "k8s-pod-200xrg1-aws", + "asset.ean": "k8s.pod:pod-200xrg1", + "asset.parents": [ + "k8s.node:node-101" + ] + }, + { + "@timestamp": "2022-02-07T00:00:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200dfp2", + "asset.name": "k8s-pod-200dfp2-aws", + "asset.ean": "k8s.pod:pod-200dfp2", + "asset.parents": [ + "k8s.node:node-101" + ] + } + ], + "onlyInB": [ + { + "@timestamp": "2022-02-07T03:00:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200wwc3", + "asset.name": "k8s-pod-200wwc3-aws", + "asset.ean": "k8s.pod:pod-200wwc3", + "asset.parents": [ + "k8s.node:node-101" + ] + }, + { + "@timestamp": "2022-02-07T03:00:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200naq4", + "asset.name": "k8s-pod-200naq4-aws", + "asset.ean": "k8s.pod:pod-200naq4", + "asset.parents": [ + "k8s.node:node-102" + ] + } + ], + "inBoth": [ + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.cluster", + "asset.id": "cluster-001", + "asset.name": "Cluster 001 (AWS EKS)", + "asset.ean": "k8s.cluster:cluster-001", + "orchestrator.type": "kubernetes", + "orchestrator.cluster.name": "Cluster 001 (AWS EKS)", + "orchestrator.cluster.id": "cluster-001", + "cloud.provider": "aws", + "cloud.region": "us-east-1", + "cloud.service.name": "eks" + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.cluster", + "asset.id": "cluster-002", + "asset.name": "Cluster 002 (Azure AKS)", + "asset.ean": "k8s.cluster:cluster-002", + "orchestrator.type": "kubernetes", + "orchestrator.cluster.name": "Cluster 002 (Azure AKS)", + "orchestrator.cluster.id": "cluster-002", + "cloud.provider": "azure", + "cloud.region": "eu-west", + "cloud.service.name": "aks" + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.node", + "asset.id": "node-101", + "asset.name": "k8s-node-101-aws", + "asset.ean": "k8s.node:node-101", + "asset.parents": [ + "k8s.cluster:cluster-001" + ], + "orchestrator.type": "kubernetes", + "orchestrator.cluster.name": "Cluster 001 (AWS EKS)", + "orchestrator.cluster.id": "cluster-001", + "cloud.provider": "aws", + "cloud.region": "us-east-1", + "cloud.service.name": "eks" + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.node", + "asset.id": "node-102", + "asset.name": "k8s-node-102-aws", + "asset.ean": "k8s.node:node-102", + "asset.parents": [ + "k8s.cluster:cluster-001" + ], + "orchestrator.type": "kubernetes", + "orchestrator.cluster.name": "Cluster 001 (AWS EKS)", + "orchestrator.cluster.id": "cluster-001", + "cloud.provider": "aws", + "cloud.region": "us-east-1", + "cloud.service.name": "eks" + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.node", + "asset.id": "node-103", + "asset.name": "k8s-node-103-aws", + "asset.ean": "k8s.node:node-103", + "asset.parents": [ + "k8s.cluster:cluster-001" + ], + "orchestrator.type": "kubernetes", + "orchestrator.cluster.name": "Cluster 001 (AWS EKS)", + "orchestrator.cluster.id": "cluster-001", + "cloud.provider": "aws", + "cloud.region": "us-east-1", + "cloud.service.name": "eks" + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200ohr5", + "asset.name": "k8s-pod-200ohr5-aws", + "asset.ean": "k8s.pod:pod-200ohr5", + "asset.parents": [ + "k8s.node:node-102" + ] + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200yyx6", + "asset.name": "k8s-pod-200yyx6-aws", + "asset.ean": "k8s.pod:pod-200yyx6", + "asset.parents": [ + "k8s.node:node-103" + ] + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200psd7", + "asset.name": "k8s-pod-200psd7-aws", + "asset.ean": "k8s.pod:pod-200psd7", + "asset.parents": [ + "k8s.node:node-103" + ] + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200wmc8", + "asset.name": "k8s-pod-200wmc8-aws", + "asset.ean": "k8s.pod:pod-200wmc8", + "asset.parents": [ + "k8s.node:node-103" + ] + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200ugg9", + "asset.name": "k8s-pod-200ugg9-aws", + "asset.ean": "k8s.pod:pod-200ugg9", + "asset.parents": [ + "k8s.node:node-103" + ] + } + ] +} +``` + +
+ +
+ +Request where each range is missing assets found in the other range, but restricted by type + +```curl +GET /assets/diff?aFrom=2022-02-07T00:00:00.000Z&aTo=2022-02-07T01:30:00.000Z&bFrom=2022-02-07T01:00:00.000Z&bTo=2022-02-07T03:00:00.000Z&type=k8s.pod + +{ + "onlyInA": [ + { + "@timestamp": "2022-02-07T00:00:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200xrg1", + "asset.name": "k8s-pod-200xrg1-aws", + "asset.ean": "k8s.pod:pod-200xrg1", + "asset.parents": [ + "k8s.node:node-101" + ] + }, + { + "@timestamp": "2022-02-07T00:00:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200dfp2", + "asset.name": "k8s-pod-200dfp2-aws", + "asset.ean": "k8s.pod:pod-200dfp2", + "asset.parents": [ + "k8s.node:node-101" + ] + } + ], + "onlyInB": [ + { + "@timestamp": "2022-02-07T03:00:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200wwc3", + "asset.name": "k8s-pod-200wwc3-aws", + "asset.ean": "k8s.pod:pod-200wwc3", + "asset.parents": [ + "k8s.node:node-101" + ] + }, + { + "@timestamp": "2022-02-07T03:00:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200naq4", + "asset.name": "k8s-pod-200naq4-aws", + "asset.ean": "k8s.pod:pod-200naq4", + "asset.parents": [ + "k8s.node:node-102" + ] + } + ], + "inBoth": [ + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200ohr5", + "asset.name": "k8s-pod-200ohr5-aws", + "asset.ean": "k8s.pod:pod-200ohr5", + "asset.parents": [ + "k8s.node:node-102" + ] + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200yyx6", + "asset.name": "k8s-pod-200yyx6-aws", + "asset.ean": "k8s.pod:pod-200yyx6", + "asset.parents": [ + "k8s.node:node-103" + ] + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200psd7", + "asset.name": "k8s-pod-200psd7-aws", + "asset.ean": "k8s.pod:pod-200psd7", + "asset.parents": [ + "k8s.node:node-103" + ] + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200wmc8", + "asset.name": "k8s-pod-200wmc8-aws", + "asset.ean": "k8s.pod:pod-200wmc8", + "asset.parents": [ + "k8s.node:node-103" + ] + }, + { + "@timestamp": "2022-02-07T01:30:00.000Z", + "asset.type": "k8s.pod", + "asset.id": "pod-200ugg9", + "asset.name": "k8s-pod-200ugg9-aws", + "asset.ean": "k8s.pod:pod-200ugg9", + "asset.parents": [ + "k8s.node:node-103" + ] + } + ] +} +``` + +
+ #### GET /assets/sample Returns the list of pre-defined sample asset documents that would be indexed diff --git a/x-pack/plugins/asset_manager/server/routes/assets.ts b/x-pack/plugins/asset_manager/server/routes/assets.ts index 391cbb7c173de..9050105380809 100644 --- a/x-pack/plugins/asset_manager/server/routes/assets.ts +++ b/x-pack/plugins/asset_manager/server/routes/assets.ts @@ -7,6 +7,7 @@ import { schema } from '@kbn/config-schema'; import { RequestHandlerContext } from '@kbn/core/server'; +import { differenceBy, intersectionBy } from 'lodash'; import { debug } from '../../common/debug_log'; import { ASSET_MANAGER_API_BASE } from '../constants'; import { getAssets } from '../lib/get_assets'; @@ -56,4 +57,79 @@ export function assetsRoutes({ router }: SetupR } } ); + + // GET /assets/diff + const assetType = schema.oneOf([ + schema.literal('k8s.pod'), + schema.literal('k8s.cluster'), + schema.literal('k8s.node'), + ]); + + const getAssetsDiffQueryOptions = schema.object({ + aFrom: schema.string(), + aTo: schema.string(), + bFrom: schema.string(), + bTo: schema.string(), + type: schema.maybe(schema.oneOf([schema.arrayOf(assetType), assetType])), + }); + router.get( + { + path: `${ASSET_MANAGER_API_BASE}/assets/diff`, + validate: { + query: getAssetsDiffQueryOptions, + }, + }, + async (context, req, res) => { + const { aFrom, aTo, bFrom, bTo, type } = req.query; + + if (new Date(aFrom) > new Date(aTo)) { + return res.badRequest({ + body: `Time range cannot move backwards in time. "aTo" (${aTo}) is before "aFrom" (${aFrom}).`, + }); + } + + if (new Date(bFrom) > new Date(bTo)) { + return res.badRequest({ + body: `Time range cannot move backwards in time. "bTo" (${bTo}) is before "bFrom" (${bFrom}).`, + }); + } + + const esClient = await getEsClientFromContext(context); + + try { + const resultsForA = await getAssets({ + esClient, + filters: { + from: aFrom, + to: aTo, + type, + }, + }); + + const resultsForB = await getAssets({ + esClient, + filters: { + from: bFrom, + to: bTo, + type, + }, + }); + + const onlyInA = differenceBy(resultsForA, resultsForB, 'asset.ean'); + const onlyInB = differenceBy(resultsForB, resultsForA, 'asset.ean'); + const inBoth = intersectionBy(resultsForA, resultsForB, 'asset.ean'); + + return res.ok({ + body: { + onlyInA, + onlyInB, + inBoth, + }, + }); + } catch (error: unknown) { + debug('error looking up asset records', error); + return res.customError({ statusCode: 500 }); + } + } + ); } diff --git a/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx index ddac56a5a7731..d6988117d63b2 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx @@ -144,17 +144,17 @@ export const WorkpadHeader: FC = ({ { iconType: 'visText', label: elementStrings.markdown.displayName, - onClick: () => createElement('markdown'), + onClick: createElement('markdown'), }, { iconType: 'node', label: elementStrings.shape.displayName, - onClick: () => createElement('shape'), + onClick: createElement('shape'), }, { iconType: 'image', label: elementStrings.image.displayName, - onClick: () => createElement('image'), + onClick: createElement('image'), }, ]; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/layout/findings_layout.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/layout/findings_layout.tsx index fecc0327b68ff..ae4d243e4b164 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/layout/findings_layout.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/layout/findings_layout.tsx @@ -149,6 +149,7 @@ const baseColumns = [ defaultMessage: 'Rule Number', } ), + sortable: true, width: '120px', }, { diff --git a/x-pack/plugins/enterprise_search/common/types/connectors.ts b/x-pack/plugins/enterprise_search/common/types/connectors.ts index be05831daa85a..69b9a81295d78 100644 --- a/x-pack/plugins/enterprise_search/common/types/connectors.ts +++ b/x-pack/plugins/enterprise_search/common/types/connectors.ts @@ -149,6 +149,7 @@ export interface Connector { language: string | null; last_seen: string | null; last_sync_error: string | null; + last_sync_scheduled_at: string | null; last_sync_status: SyncStatus | null; last_synced: string | null; name: string; @@ -173,9 +174,9 @@ export interface ConnectorSyncJob { filtering: FilteringRules | FilteringRules[] | null; id: string; index_name: string; - language: string; + language: string | null; pipeline: IngestPipelineParams | null; - service_type: string; + service_type: string | null; }; created_at: string; deleted_document_count: number; @@ -183,12 +184,12 @@ export interface ConnectorSyncJob { id: string; indexed_document_count: number; indexed_document_volume: number; - last_seen: string; + last_seen: string | null; metadata: Record; - started_at: string; + started_at: string | null; status: SyncStatus; trigger_method: TriggerMethod; - worker_hostname: string; + worker_hostname: string | null; } export type ConnectorSyncJobDocument = Omit; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts index 955cf219a8019..4db81ef3bbf0e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/search_indices.mock.ts @@ -102,6 +102,7 @@ export const indices: ElasticsearchIndexWithIngestion[] = [ language: 'en', last_seen: null, last_sync_error: null, + last_sync_scheduled_at: null, last_sync_status: SyncStatus.COMPLETED, last_synced: null, name: 'connector', @@ -197,6 +198,7 @@ export const indices: ElasticsearchIndexWithIngestion[] = [ language: 'en', last_seen: null, last_sync_error: null, + last_sync_scheduled_at: null, last_sync_status: SyncStatus.COMPLETED, last_synced: null, name: 'crawler', diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts index 78ef10664abcb..8414a655a1106 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/__mocks__/view_index.mock.ts @@ -112,6 +112,7 @@ export const connectorIndex: ConnectorViewIndex = { language: 'en', last_seen: null, last_sync_error: null, + last_sync_scheduled_at: null, last_sync_status: SyncStatus.COMPLETED, last_synced: null, name: 'connector', @@ -211,6 +212,7 @@ export const crawlerIndex: CrawlerViewIndex = { language: 'en', last_seen: null, last_sync_error: null, + last_sync_scheduled_at: null, last_sync_status: SyncStatus.COMPLETED, last_synced: null, name: 'crawler', diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_job_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_job_flyout.tsx index 95fbefa8c773b..b3257fa6455e8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_job_flyout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_job_flyout.tsx @@ -85,9 +85,9 @@ export const SyncJobFlyout: React.FC = ({ onClose, syncJob } canceledAt={syncJob.canceled_at ?? ''} cancelationRequestedAt={syncJob.cancelation_requested_at ?? ''} syncRequestedAt={syncJob.created_at} - syncStarted={syncJob.started_at} + syncStarted={syncJob.started_at ?? ''} completed={syncJob.completed_at ?? ''} - lastUpdated={syncJob.last_seen} + lastUpdated={syncJob.last_seen ?? ''} triggerMethod={syncJob.trigger_method} />
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_table.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_table.tsx index c73f567f31edb..a6f953a2c6f5d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_table.tsx @@ -16,8 +16,11 @@ import { EuiIcon, EuiText, } from '@elastic/eui'; + import { i18n } from '@kbn/i18n'; +import { NATIVE_CONNECTOR_DEFINITIONS } from '../../../../../common/connectors/native_connectors'; + import { Meta } from '../../../../../common/types'; import { healthColorsMap } from '../../../shared/constants/health_colors'; import { generateEncodedPath } from '../../../shared/encode_path_params'; @@ -26,8 +29,8 @@ import { EuiLinkTo } from '../../../shared/react_router_helpers'; import { EuiBadgeTo } from '../../../shared/react_router_helpers/eui_components'; import { convertMetaToPagination } from '../../../shared/table_pagination'; import { SEARCH_INDEX_PATH } from '../../routes'; -import { ElasticsearchViewIndex, IngestionMethod } from '../../types'; -import { ingestionMethodToText } from '../../utils/indices'; +import { ElasticsearchViewIndex } from '../../types'; +import { ingestionMethodToText, isConnectorIndex } from '../../utils/indices'; import { ingestionStatusToColor, ingestionStatusToText, @@ -64,8 +67,7 @@ export const IndicesTable: React.FC = ({ ), sortable: true, - truncateText: true, - width: '40%', + width: '33%', }, { field: 'health', @@ -80,7 +82,6 @@ export const IndicesTable: React.FC = ({ ), sortable: true, truncateText: true, - width: '10%', }, { field: 'count', @@ -89,21 +90,43 @@ export const IndicesTable: React.FC = ({ }), sortable: true, truncateText: true, - width: '10%', }, { - field: 'ingestionMethod', + name: i18n.translate( + 'xpack.enterpriseSearch.content.searchIndices.ingestionName.columnTitle', + { + defaultMessage: 'Ingestion name', + } + ), + render: (index: ElasticsearchViewIndex) => ( + + {(isConnectorIndex(index) && + index.connector.service_type && + NATIVE_CONNECTOR_DEFINITIONS[index.connector.service_type]?.name) ?? + '--'} + + ), + truncateText: true, + }, + { name: i18n.translate( 'xpack.enterpriseSearch.content.searchIndices.ingestionMethod.columnTitle', { defaultMessage: 'Ingestion method', } ), - render: (ingestionMethod: IngestionMethod) => ( - {ingestionMethodToText(ingestionMethod)} + render: (index: ElasticsearchViewIndex) => ( + + {isConnectorIndex(index) && index.connector.is_native + ? i18n.translate( + 'xpack.enterpriseSearch.content.searchIndices.ingestionmethod.nativeConnector', + { + defaultMessage: 'Native connector', + } + ) + : ingestionMethodToText(index.ingestionMethod)} + ), - truncateText: true, - width: '10%', }, { name: i18n.translate( @@ -124,7 +147,6 @@ export const IndicesTable: React.FC = ({ ); }, truncateText: true, - width: '15%', }, { actions: [ @@ -182,7 +204,6 @@ export const IndicesTable: React.FC = ({ name: i18n.translate('xpack.enterpriseSearch.content.searchIndices.actions.columnTitle', { defaultMessage: 'Actions', }), - width: '10%', }, ]; return ( diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx index 7d328dec9887f..15d58360ff92f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx @@ -99,7 +99,7 @@ export const SearchIndices: React.FC = () => { {i18n.translate( 'xpack.enterpriseSearch.content.searchIndices.create.buttonTitle', { - defaultMessage: 'Create new index', + defaultMessage: 'Create a new index', } )} diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.test.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.test.ts index d51db7398d146..0ba209c814f73 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.test.ts @@ -146,6 +146,7 @@ describe('addConnector lib function', () => { language: 'fr', last_seen: null, last_sync_error: null, + last_sync_scheduled_at: null, last_sync_status: null, last_synced: null, name: 'index_name', @@ -328,6 +329,7 @@ describe('addConnector lib function', () => { language: null, last_seen: null, last_sync_error: null, + last_sync_scheduled_at: null, last_sync_status: null, last_synced: null, name: 'index_name', @@ -435,6 +437,7 @@ describe('addConnector lib function', () => { language: 'en', last_seen: null, last_sync_error: null, + last_sync_scheduled_at: null, last_sync_status: null, last_synced: null, name: 'index_name', diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts index 25956b271245a..f9dedfe069170 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.test.ts @@ -7,19 +7,19 @@ import { IScopedClusterClient } from '@kbn/core/server'; -import { CONNECTORS_INDEX } from '../..'; +import { CONNECTORS_INDEX, CONNECTORS_JOBS_INDEX } from '../..'; +import { SyncStatus, TriggerMethod } from '../../../common/types/connectors'; + import { ErrorCode } from '../../../common/types/error_codes'; import { startConnectorSync } from './start_sync'; -describe('addConnector lib function', () => { +describe('startSync lib function', () => { const mockClient = { asCurrentUser: { get: jest.fn(), index: jest.fn(), - indices: { - refresh: jest.fn(), - }, + update: jest.fn(), }, asInternalUser: {}, }; @@ -31,6 +31,7 @@ describe('addConnector lib function', () => { it('should start a sync', async () => { mockClient.asCurrentUser.get.mockImplementationOnce(() => { return Promise.resolve({ + _id: 'connectorId', _source: { api_key_id: null, configuration: {}, @@ -38,8 +39,10 @@ describe('addConnector lib function', () => { custom_scheduling: {}, error: null, index_name: 'index_name', + language: null, last_seen: null, last_sync_error: null, + last_sync_scheduled_at: null, last_sync_status: null, last_synced: null, scheduling: { enabled: true, interval: '1 2 3 4 5' }, @@ -57,26 +60,93 @@ describe('addConnector lib function', () => { ).resolves.toEqual({ _id: 'fakeId' }); expect(mockClient.asCurrentUser.index).toHaveBeenCalledWith({ document: { - api_key_id: null, - configuration: {}, - created_at: null, - custom_scheduling: {}, + cancelation_requested_at: null, + canceled_at: null, + completed_at: null, + connector: { + configuration: {}, + filtering: null, + id: 'connectorId', + index_name: 'index_name', + language: null, + pipeline: null, + service_type: null, + }, + created_at: expect.any(String), + deleted_document_count: 0, error: null, - index_name: 'index_name', + indexed_document_count: 0, + indexed_document_volume: 0, last_seen: null, - last_sync_error: null, - last_sync_status: null, - last_synced: null, - scheduling: { enabled: true, interval: '1 2 3 4 5' }, - service_type: null, - status: 'not connected', - sync_now: true, + metadata: {}, + started_at: null, + status: SyncStatus.PENDING, + trigger_method: TriggerMethod.ON_DEMAND, + worker_hostname: null, }, - id: 'connectorId', - index: CONNECTORS_INDEX, + index: CONNECTORS_JOBS_INDEX, }); - expect(mockClient.asCurrentUser.indices.refresh).toHaveBeenCalledWith({ - index: CONNECTORS_INDEX, + }); + it('should start a sync with service type, pipeline and nextSyncConfig', async () => { + mockClient.asCurrentUser.get.mockImplementationOnce(() => { + return Promise.resolve({ + _source: { + api_key_id: null, + configuration: { config: { label: 'label', value: 'haha' } }, + created_at: null, + custom_scheduling: {}, + error: null, + filtering: [{ active: 'filtering' }], + index_name: 'index_name', + language: 'nl', + last_seen: null, + last_sync_error: null, + last_sync_status: null, + last_synced: null, + pipeline: { name: 'pipeline' }, + scheduling: { enabled: true, interval: '1 2 3 4 5' }, + service_type: 'service_type', + status: 'not connected', + sync_now: false, + }, + index: CONNECTORS_INDEX, + }); + }); + mockClient.asCurrentUser.index.mockImplementation(() => ({ _id: 'fakeId' })); + + await expect( + startConnectorSync(mockClient as unknown as IScopedClusterClient, 'connectorId', 'syncConfig') + ).resolves.toEqual({ _id: 'fakeId' }); + expect(mockClient.asCurrentUser.index).toHaveBeenCalledWith({ + document: { + cancelation_requested_at: null, + canceled_at: null, + completed_at: null, + connector: { + configuration: { + config: { label: 'label', value: 'haha' }, + nextSyncConfig: { label: 'nextSyncConfig', value: 'syncConfig' }, + }, + filtering: 'filtering', + id: 'connectorId', + index_name: 'index_name', + language: 'nl', + pipeline: { name: 'pipeline' }, + service_type: 'service_type', + }, + created_at: expect.any(String), + deleted_document_count: 0, + error: null, + indexed_document_count: 0, + indexed_document_volume: 0, + last_seen: null, + metadata: {}, + started_at: null, + status: SyncStatus.PENDING, + trigger_method: TriggerMethod.ON_DEMAND, + worker_hostname: null, + }, + index: CONNECTORS_JOBS_INDEX, }); }); @@ -89,4 +159,52 @@ describe('addConnector lib function', () => { ).rejects.toEqual(new Error(ErrorCode.RESOURCE_NOT_FOUND)); expect(mockClient.asCurrentUser.index).not.toHaveBeenCalled(); }); + + it('should set sync_now for crawler and not index a sync job', async () => { + mockClient.asCurrentUser.get.mockImplementationOnce(() => { + return Promise.resolve({ + _primary_term: 1, + _seq_no: 10, + _source: { + api_key_id: null, + configuration: { config: { label: 'label', value: 'haha' } }, + created_at: null, + custom_scheduling: {}, + error: null, + filtering: [{ active: 'filtering' }], + index_name: 'index_name', + language: 'nl', + last_seen: null, + last_sync_error: null, + last_sync_status: null, + last_synced: null, + pipeline: { name: 'pipeline' }, + scheduling: { enabled: true, interval: '1 2 3 4 5' }, + service_type: 'elastic-crawler', + status: 'not connected', + sync_now: false, + }, + index: CONNECTORS_INDEX, + }); + }); + mockClient.asCurrentUser.update.mockImplementation(() => ({ _id: 'fakeId' })); + + await expect( + startConnectorSync(mockClient as unknown as IScopedClusterClient, 'connectorId', 'syncConfig') + ).resolves.toEqual({ _id: 'fakeId' }); + expect(mockClient.asCurrentUser.index).not.toHaveBeenCalled(); + expect(mockClient.asCurrentUser.update).toHaveBeenCalledWith({ + doc: { + configuration: { + config: { label: 'label', value: 'haha' }, + nextSyncConfig: { label: 'nextSyncConfig', value: 'syncConfig' }, + }, + sync_now: true, + }, + id: 'connectorId', + if_primary_term: 1, + if_seq_no: 10, + index: CONNECTORS_INDEX, + }); + }); }); diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts index 68b9adf0aee79..ca1d3b19a2c3a 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts @@ -7,9 +7,16 @@ import { IScopedClusterClient } from '@kbn/core/server'; -import { CONNECTORS_INDEX } from '../..'; +import { CONNECTORS_INDEX, CONNECTORS_JOBS_INDEX } from '../..'; +import { ENTERPRISE_SEARCH_CONNECTOR_CRAWLER_SERVICE_TYPE } from '../../../common/constants'; -import { ConnectorDocument } from '../../../common/types/connectors'; +import { + ConnectorConfiguration, + ConnectorDocument, + ConnectorSyncJobDocument, + SyncStatus, + TriggerMethod, +} from '../../../common/types/connectors'; import { ErrorCode } from '../../../common/types/error_codes'; export const startConnectorSync = async ( @@ -23,21 +30,57 @@ export const startConnectorSync = async ( }); const connector = connectorResult._source; if (connector) { - if (nextSyncConfig) { - connector.configuration.nextSyncConfig = { label: 'nextSyncConfig', value: nextSyncConfig }; + const configuration: ConnectorConfiguration = nextSyncConfig + ? { + ...connector.configuration, + nextSyncConfig: { label: 'nextSyncConfig', value: nextSyncConfig }, + } + : connector.configuration; + const { filtering, index_name, language, pipeline, service_type } = connector; + + const now = new Date().toISOString(); + + if (connector.service_type === ENTERPRISE_SEARCH_CONNECTOR_CRAWLER_SERVICE_TYPE) { + return await client.asCurrentUser.update({ + doc: { + configuration, + sync_now: true, + }, + id: connectorId, + if_primary_term: connectorResult._primary_term, + if_seq_no: connectorResult._seq_no, + index: CONNECTORS_INDEX, + }); } - const result = await client.asCurrentUser.index({ + return await client.asCurrentUser.index({ document: { - ...connector, - sync_now: true, + cancelation_requested_at: null, + canceled_at: null, + completed_at: null, + connector: { + configuration, + filtering: filtering ? filtering[0]?.active ?? null : null, + id: connectorId, + index_name, + language, + pipeline: pipeline ?? null, + service_type, + }, + created_at: now, + deleted_document_count: 0, + error: null, + indexed_document_count: 0, + indexed_document_volume: 0, + last_seen: null, + metadata: {}, + started_at: null, + status: SyncStatus.PENDING, + trigger_method: TriggerMethod.ON_DEMAND, + worker_hostname: null, }, - id: connectorId, - index: CONNECTORS_INDEX, + index: CONNECTORS_JOBS_INDEX, }); - - await client.asCurrentUser.indices.refresh({ index: CONNECTORS_INDEX }); - return result; } else { throw new Error(ErrorCode.RESOURCE_NOT_FOUND); } diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/update_connector_scheduling.test.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/update_connector_scheduling.test.ts index 2fa4c0954b245..9a66a71b26e9a 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/update_connector_scheduling.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/update_connector_scheduling.test.ts @@ -39,6 +39,7 @@ describe('addConnector lib function', () => { index_name: 'index_name', last_seen: null, last_sync_error: null, + last_sync_scheduled_at: null, last_sync_status: null, last_synced: null, scheduling: { enabled: false, interval: '* * * * *' }, @@ -67,6 +68,7 @@ describe('addConnector lib function', () => { index_name: 'index_name', last_seen: null, last_sync_error: null, + last_sync_scheduled_at: null, last_sync_status: null, last_synced: null, scheduling: { enabled: true, interval: '1 2 3 4 5' }, diff --git a/x-pack/plugins/enterprise_search/server/lib/crawler/post_connector.test.ts b/x-pack/plugins/enterprise_search/server/lib/crawler/post_connector.test.ts index 729b72e8f643d..95fa56e8ff6ba 100644 --- a/x-pack/plugins/enterprise_search/server/lib/crawler/post_connector.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/crawler/post_connector.test.ts @@ -92,6 +92,7 @@ describe('recreateConnectorDocument lib function', () => { language: '', last_seen: null, last_sync_error: null, + last_sync_scheduled_at: null, last_sync_status: null, last_synced: null, name: 'indexName', diff --git a/x-pack/plugins/enterprise_search/server/utils/create_connector_document.test.ts b/x-pack/plugins/enterprise_search/server/utils/create_connector_document.test.ts index 62851572cf798..08e18e0907532 100644 --- a/x-pack/plugins/enterprise_search/server/utils/create_connector_document.test.ts +++ b/x-pack/plugins/enterprise_search/server/utils/create_connector_document.test.ts @@ -86,6 +86,7 @@ describe('createConnectorDocument', () => { language: 'fr', last_seen: null, last_sync_error: null, + last_sync_scheduled_at: null, last_sync_status: null, last_synced: null, name: 'indexName', @@ -177,6 +178,7 @@ describe('createConnectorDocument', () => { language: 'fr', last_seen: null, last_sync_error: null, + last_sync_scheduled_at: null, last_sync_status: null, last_synced: null, name: 'indexName', diff --git a/x-pack/plugins/enterprise_search/server/utils/create_connector_document.ts b/x-pack/plugins/enterprise_search/server/utils/create_connector_document.ts index d4776e3941cee..507559b65440d 100644 --- a/x-pack/plugins/enterprise_search/server/utils/create_connector_document.ts +++ b/x-pack/plugins/enterprise_search/server/utils/create_connector_document.ts @@ -91,6 +91,7 @@ export function createConnectorDocument({ language, last_seen: null, last_sync_error: null, + last_sync_scheduled_at: null, last_sync_status: null, last_synced: null, name: indexName.startsWith('search-') ? indexName.substring(7) : indexName, diff --git a/x-pack/plugins/fleet/common/types/models/agent.ts b/x-pack/plugins/fleet/common/types/models/agent.ts index 11f259e959102..304da5b3f6514 100644 --- a/x-pack/plugins/fleet/common/types/models/agent.ts +++ b/x-pack/plugins/fleet/common/types/models/agent.ts @@ -389,7 +389,10 @@ export interface FleetServerAgentAction { data?: { [k: string]: unknown; }; - total?: number; + + /** Trace id */ + traceparent?: string | null; + [k: string]: unknown; } diff --git a/x-pack/plugins/fleet/server/services/agents/actions.ts b/x-pack/plugins/fleet/server/services/agents/actions.ts index bc56b7e80e8af..8f39557697312 100644 --- a/x-pack/plugins/fleet/server/services/agents/actions.ts +++ b/x-pack/plugins/fleet/server/services/agents/actions.ts @@ -7,6 +7,7 @@ import { v4 as uuidv4 } from 'uuid'; import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; +import apm from 'elastic-apm-node'; import { appContextService } from '../app_context'; import type { @@ -50,6 +51,7 @@ export async function createAgentAction( minimum_execution_duration: newAgentAction.minimum_execution_duration, rollout_duration_seconds: newAgentAction.rollout_duration_seconds, total: newAgentAction.total, + traceparent: apm.currentTraceparent, }; await esClient.create({ @@ -98,6 +100,7 @@ export async function bulkCreateAgentActions( action_id: action.id, data: action.data, type: action.type, + traceparent: apm.currentTraceparent, }; return [ diff --git a/x-pack/plugins/fleet/server/services/audit_logging.ts b/x-pack/plugins/fleet/server/services/audit_logging.ts index a9549ade160d1..efadaedfb5463 100644 --- a/x-pack/plugins/fleet/server/services/audit_logging.ts +++ b/x-pack/plugins/fleet/server/services/audit_logging.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { AuditLogger } from '@kbn/security-plugin/server'; +import type { AuditEvent, AuditLogger } from '@kbn/security-plugin/server'; import { appContextService } from './app_context'; import { getRequestStore } from './request_store'; @@ -14,8 +14,12 @@ class AuditLoggingService { /** * Write a custom audit log record. If a current request is available, the log will include * user/session data. If not, an unscoped audit logger will be used. + * + * Note: all Fleet audit logs written via this method will have a `labels.application` value + * of `elastic/fleet`. Consumers aren't able to override this value, and a custom `labels.application` + * value provided as an argument will be overwritten. */ - public writeCustomAuditLog(...args: Parameters) { + public writeCustomAuditLog(args: AuditEvent) { const securitySetup = appContextService.getSecuritySetup(); let auditLogger: AuditLogger | undefined; @@ -27,7 +31,7 @@ class AuditLoggingService { auditLogger = securitySetup.audit.withoutRequest; } - auditLogger.log(...args); + auditLogger.log({ ...args, labels: { ...args.labels, application: 'elastic/fleet' } }); } /** diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap b/x-pack/plugins/infra/public/alerting/metric_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap index 1278487a52c4a..9994945cd3290 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`AlertDetailsAppSection should render annotation 1`] = ` +exports[`AlertDetailsAppSection should render annotations 1`] = ` Array [ Object { "annotations": Array [ @@ -8,7 +8,12 @@ Array [ alertStart={1678716383695} color="#BD271E" dateFormat="YYYY-MM-DD HH:mm" - id="annotation_alert_start" + id="alert_start_annotation" + />, + , ], "chartType": "line", diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_details_app_section.test.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_details_app_section.test.tsx index 0071ef93f54b6..20f134633e04b 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_details_app_section.test.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_details_app_section.test.tsx @@ -19,7 +19,11 @@ import { ExpressionChart } from './expression_chart'; jest.mock('@kbn/observability-alert-details', () => ({ AlertAnnotation: () => {}, - getAlertTimeRange: () => ({ from: '2023-03-28T10:43:13.802Z', to: '2023-03-29T13:14:09.581Z' }), + AlertActiveTimeRangeAnnotation: () => {}, + getPaddedAlertTimeRange: () => ({ + from: '2023-03-28T10:43:13.802Z', + to: '2023-03-29T13:14:09.581Z', + }), })); jest.mock('./expression_chart', () => ({ @@ -61,7 +65,7 @@ describe('AlertDetailsAppSection', () => { expect((await result.findByTestId('metricThresholdAppSection')).children.length).toBe(3); }); - it('should render annotation', async () => { + it('should render annotations', async () => { const mockedExpressionChart = jest.fn(() =>
); (ExpressionChart as jest.Mock).mockImplementation(mockedExpressionChart); renderComponent(); diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx index 3223a0c10362a..1bc9ddb33e8c4 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx @@ -6,11 +6,16 @@ */ import React, { useMemo } from 'react'; +import moment from 'moment'; import { EuiFlexGroup, EuiFlexItem, EuiPanel, useEuiTheme } from '@elastic/eui'; import { TopAlert } from '@kbn/observability-plugin/public'; import { ALERT_END, ALERT_START } from '@kbn/rule-data-utils'; import { Rule } from '@kbn/alerting-plugin/common'; -import { AlertAnnotation, getAlertTimeRange } from '@kbn/observability-alert-details'; +import { + AlertAnnotation, + getPaddedAlertTimeRange, + AlertActiveTimeRangeAnnotation, +} from '@kbn/observability-alert-details'; import { useSourceContext, withSourceProvider } from '../../../containers/metrics_source'; import { generateUniqueKey } from '../lib/generate_unique_key'; import { MetricsExplorerChartType } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options'; @@ -28,7 +33,8 @@ export type MetricThresholdRule = Rule< export type MetricThresholdAlert = TopAlert; const DEFAULT_DATE_FORMAT = 'YYYY-MM-DD HH:mm'; -const ALERT_START_ANNOTATION_ID = 'annotation_alert_start'; +const ALERT_START_ANNOTATION_ID = 'alert_start_annotation'; +const ALERT_TIME_RANGE_ANNOTATION_ID = 'alert_time_range_annotation'; interface AppSectionProps { rule: MetricThresholdRule; @@ -44,7 +50,8 @@ export function AlertDetailsAppSection({ alert, rule }: AppSectionProps) { () => createDerivedIndexPattern(), [createDerivedIndexPattern] ); - const timeRange = getAlertTimeRange(alert.fields[ALERT_START]!, alert.fields[ALERT_END]); + const timeRange = getPaddedAlertTimeRange(alert.fields[ALERT_START]!, alert.fields[ALERT_END]); + const alertEnd = alert.fields[ALERT_END] ? moment(alert.fields[ALERT_END]).valueOf() : undefined; const annotations = [ , + , ]; return !!rule.params.criteria ? ( diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts index bf9ac7006e1c2..27f19e6c6cf9a 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts @@ -5,6 +5,8 @@ * 2.0. */ +// please try to keep this list sorted by module name (e.g. './bar' before './foo') + export { Append } from './append'; export { Bytes } from './bytes'; export { Circle } from './circle'; @@ -38,12 +40,12 @@ export { Rename } from './rename'; export { Script } from './script'; export { SetProcessor } from './set'; export { SetSecurityUser } from './set_security_user'; -export { Split } from './split'; export { Sort } from './sort'; +export { Split } from './split'; export { Trim } from './trim'; export { Uppercase } from './uppercase'; +export { UriParts } from './uri_parts'; export { UrlDecode } from './url_decode'; export { UserAgent } from './user_agent'; -export { UriParts } from './uri_parts'; export type { FormFieldsComponent } from './shared'; diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx index 787c6766592ab..c127e9f1130d3 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx @@ -13,6 +13,7 @@ import { LensByReferenceInput, LensSavedObjectAttributes, LensUnwrapResult, + LensEmbeddableDeps, } from './embeddable'; import { ReactExpressionRendererProps } from '@kbn/expressions-plugin/public'; import { spacesPluginMock } from '@kbn/spaces-plugin/public/mocks'; @@ -149,49 +150,53 @@ describe('embeddable', () => { mountpoint.remove(); }); + function getEmbeddableProps(props: Partial = {}): LensEmbeddableDeps { + return { + timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, + attributeService, + data: dataMock, + uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, + inspector: inspectorPluginMock.createStartContract(), + expressionRenderer, + coreStart: {} as CoreStart, + basePath, + dataViews: { + get: (id: string) => Promise.resolve({ id, isTimeBased: () => false }), + } as unknown as DataViewsContract, + capabilities: { + canSaveDashboards: true, + canSaveVisualizations: true, + discover: {}, + navLinks: {}, + }, + getTrigger, + visualizationMap: defaultVisualizationMap, + datasourceMap: defaultDatasourceMap, + injectFilterReferences: jest.fn(mockInjectFilterReferences), + theme: themeServiceMock.createStartContract(), + documentToExpression: () => + Promise.resolve({ + ast: { + type: 'expression', + chain: [ + { type: 'function', function: 'my', arguments: {} }, + { type: 'function', function: 'expression', arguments: {} }, + ], + }, + indexPatterns: {}, + indexPatternRefs: [], + }), + ...props, + }; + } + it('should render expression once with expression renderer', async () => { - const embeddable = new Embeddable( - { - timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, - attributeService, - data: dataMock, - expressionRenderer, - coreStart: {} as CoreStart, - basePath, - dataViews: {} as DataViewsContract, - capabilities: { - canSaveDashboards: true, - canSaveVisualizations: true, - discover: {}, - navLinks: {}, - }, - inspector: inspectorPluginMock.createStartContract(), - getTrigger, - theme: themeServiceMock.createStartContract(), - visualizationMap: defaultVisualizationMap, - datasourceMap: defaultDatasourceMap, - injectFilterReferences: jest.fn(mockInjectFilterReferences), - documentToExpression: () => - Promise.resolve({ - ast: { - type: 'expression', - chain: [ - { type: 'function', function: 'my', arguments: {} }, - { type: 'function', function: 'expression', arguments: {} }, - ], - }, - indexPatterns: {}, - indexPatternRefs: [], - }), - uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, + const embeddable = new Embeddable(getEmbeddableProps(), { + timeRange: { + from: 'now-15m', + to: 'now', }, - { - timeRange: { - from: 'now-15m', - to: 'now', - }, - } as LensEmbeddableInput - ); + } as LensEmbeddableInput); embeddable.render(mountpoint); // wait one tick to give embeddable time to initialize @@ -203,48 +208,12 @@ describe('embeddable', () => { }); it('should not throw if render is called after destroy', async () => { - const embeddable = new Embeddable( - { - timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, - attributeService, - data: dataMock, - expressionRenderer, - coreStart: {} as CoreStart, - basePath, - dataViews: {} as DataViewsContract, - capabilities: { - canSaveDashboards: true, - canSaveVisualizations: true, - discover: {}, - navLinks: {}, - }, - inspector: inspectorPluginMock.createStartContract(), - getTrigger, - theme: themeServiceMock.createStartContract(), - visualizationMap: defaultVisualizationMap, - datasourceMap: defaultDatasourceMap, - injectFilterReferences: jest.fn(mockInjectFilterReferences), - documentToExpression: () => - Promise.resolve({ - ast: { - type: 'expression', - chain: [ - { type: 'function', function: 'my', arguments: {} }, - { type: 'function', function: 'expression', arguments: {} }, - ], - }, - indexPatterns: {}, - indexPatternRefs: [], - }), - uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, + const embeddable = new Embeddable(getEmbeddableProps(), { + timeRange: { + from: 'now-15m', + to: 'now', }, - { - timeRange: { - from: 'now-15m', - to: 'now', - }, - } as LensEmbeddableInput - ); + } as LensEmbeddableInput); let renderCalled = false; let renderThrew = false; // destroying completes output synchronously which might make a synchronous render call - this shouldn't throw @@ -263,48 +232,12 @@ describe('embeddable', () => { }); it('should render once even if reload is called before embeddable is fully initialized', async () => { - const embeddable = new Embeddable( - { - timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, - attributeService, - data: dataMock, - uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, - expressionRenderer, - coreStart: {} as CoreStart, - basePath, - dataViews: {} as DataViewsContract, - inspector: inspectorPluginMock.createStartContract(), - capabilities: { - canSaveDashboards: true, - canSaveVisualizations: true, - discover: {}, - navLinks: {}, - }, - getTrigger, - visualizationMap: defaultVisualizationMap, - datasourceMap: defaultDatasourceMap, - injectFilterReferences: jest.fn(mockInjectFilterReferences), - theme: themeServiceMock.createStartContract(), - documentToExpression: () => - Promise.resolve({ - ast: { - type: 'expression', - chain: [ - { type: 'function', function: 'my', arguments: {} }, - { type: 'function', function: 'expression', arguments: {} }, - ], - }, - indexPatterns: {}, - indexPatternRefs: [], - }), + const embeddable = new Embeddable(getEmbeddableProps(), { + timeRange: { + from: 'now-15m', + to: 'now', }, - { - timeRange: { - from: 'now-15m', - to: 'now', - }, - } as LensEmbeddableInput - ); + } as LensEmbeddableInput); await embeddable.reload(); expect(expressionRenderer).toHaveBeenCalledTimes(0); embeddable.render(mountpoint); @@ -317,43 +250,7 @@ describe('embeddable', () => { }); it('should not render the visualization if any error arises', async () => { - const embeddable = new Embeddable( - { - timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, - attributeService, - data: dataMock, - uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, - expressionRenderer, - coreStart: {} as CoreStart, - basePath, - inspector: inspectorPluginMock.createStartContract(), - dataViews: {} as DataViewsContract, - capabilities: { - canSaveDashboards: true, - canSaveVisualizations: true, - discover: {}, - navLinks: {}, - }, - getTrigger, - visualizationMap: defaultVisualizationMap, - datasourceMap: defaultDatasourceMap, - injectFilterReferences: jest.fn(mockInjectFilterReferences), - theme: themeServiceMock.createStartContract(), - documentToExpression: () => - Promise.resolve({ - ast: { - type: 'expression', - chain: [ - { type: 'function', function: 'my', arguments: {} }, - { type: 'function', function: 'expression', arguments: {} }, - ], - }, - indexPatterns: {}, - indexPatternRefs: [], - }), - }, - {} as LensEmbeddableInput - ); + const embeddable = new Embeddable(getEmbeddableProps(), {} as LensEmbeddableInput); jest.spyOn(embeddable, 'getUserMessages').mockReturnValue([ { @@ -393,41 +290,10 @@ describe('embeddable', () => { <>getEmbeddableLegacyUrlConflict )); const embeddable = new Embeddable( - { - timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, - attributeService, - data: dataMock, - uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, - inspector: inspectorPluginMock.createStartContract(), - expressionRenderer, - coreStart: {} as CoreStart, - basePath, - dataViews: {} as DataViewsContract, + getEmbeddableProps({ spaces: spacesPluginStart, - capabilities: { - canSaveDashboards: true, - canSaveVisualizations: true, - discover: {}, - navLinks: {}, - }, - getTrigger, - visualizationMap: defaultVisualizationMap, - datasourceMap: defaultDatasourceMap, - injectFilterReferences: jest.fn(mockInjectFilterReferences), - theme: themeServiceMock.createStartContract(), - documentToExpression: () => - Promise.resolve({ - ast: { - type: 'expression', - chain: [ - { type: 'function', function: 'my', arguments: {} }, - { type: 'function', function: 'expression', arguments: {} }, - ], - }, - indexPatterns: {}, - indexPatternRefs: [], - }), - }, + attributeService, + }), {} as LensEmbeddableInput ); await embeddable.initializeSavedVis({} as LensEmbeddableInput); @@ -437,14 +303,50 @@ describe('embeddable', () => { }); it('should not render if timeRange prop is not passed when a referenced data view is time based', async () => { - attributeService = attributeServiceMockFromSavedVis({ - ...savedVis, - references: [ - { type: 'index-pattern', id: '123', name: 'abc' }, - { type: 'index-pattern', id: '123', name: 'def' }, - { type: 'index-pattern', id: '456', name: 'ghi' }, - ], - }); + const embeddable = new Embeddable( + getEmbeddableProps({ + attributeService: attributeServiceMockFromSavedVis({ + ...savedVis, + references: [ + { type: 'index-pattern', id: '123', name: 'abc' }, + { type: 'index-pattern', id: '123', name: 'def' }, + { type: 'index-pattern', id: '456', name: 'ghi' }, + ], + }), + dataViews: { + get: (id: string) => Promise.resolve({ id, isTimeBased: () => true }), + } as unknown as DataViewsContract, + }), + {} as LensEmbeddableInput + ); + await embeddable.initializeSavedVis({} as LensEmbeddableInput); + embeddable.render(mountpoint); + expect(expressionRenderer).toHaveBeenCalledTimes(0); + }); + + it('should initialize output with deduped list of index patterns', async () => { + const embeddable = new Embeddable( + getEmbeddableProps({ + attributeService: attributeServiceMockFromSavedVis({ + ...savedVis, + references: [ + { type: 'index-pattern', id: '123', name: 'abc' }, + { type: 'index-pattern', id: '123', name: 'def' }, + { type: 'index-pattern', id: '456', name: 'ghi' }, + ], + }), + }), + {} as LensEmbeddableInput + ); + + await embeddable.initializeSavedVis({} as LensEmbeddableInput); + const outputIndexPatterns = embeddable.getOutput().indexPatterns!; + expect(outputIndexPatterns.length).toEqual(2); + expect(outputIndexPatterns[0].id).toEqual('123'); + expect(outputIndexPatterns[1].id).toEqual('456'); + }); + + it('should re-render once on filter change', async () => { const embeddable = new Embeddable( { timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, @@ -455,9 +357,7 @@ describe('embeddable', () => { coreStart: {} as CoreStart, basePath, inspector: inspectorPluginMock.createStartContract(), - dataViews: { - get: (id: string) => Promise.resolve({ id, isTimeBased: jest.fn(() => true) }), - } as unknown as DataViewsContract, + dataViews: {} as DataViewsContract, capabilities: { canSaveDashboards: true, canSaveVisualizations: true, @@ -482,22 +382,23 @@ describe('embeddable', () => { indexPatternRefs: [], }), }, - {} as LensEmbeddableInput + { id: '123' } as LensEmbeddableInput ); - await embeddable.initializeSavedVis({} as LensEmbeddableInput); + await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); embeddable.render(mountpoint); - expect(expressionRenderer).toHaveBeenCalledTimes(0); - }); - it('should initialize output with deduped list of index patterns', async () => { - attributeService = attributeServiceMockFromSavedVis({ - ...savedVis, - references: [ - { type: 'index-pattern', id: '123', name: 'abc' }, - { type: 'index-pattern', id: '123', name: 'def' }, - { type: 'index-pattern', id: '456', name: 'ghi' }, - ], + expect(expressionRenderer).toHaveBeenCalledTimes(1); + + embeddable.updateInput({ + filters: [{ meta: { alias: 'test', negate: false, disabled: false } }], }); + + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(expressionRenderer).toHaveBeenCalledTimes(2); + }); + + it('should re-render once on search session change', async () => { const embeddable = new Embeddable( { timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, @@ -508,9 +409,7 @@ describe('embeddable', () => { coreStart: {} as CoreStart, basePath, inspector: inspectorPluginMock.createStartContract(), - dataViews: { - get: (id: string) => Promise.resolve({ id, isTimeBased: () => false }), - } as unknown as DataViewsContract, + dataViews: {} as DataViewsContract, capabilities: { canSaveDashboards: true, canSaveVisualizations: true, @@ -535,104 +434,7 @@ describe('embeddable', () => { indexPatternRefs: [], }), }, - {} as LensEmbeddableInput - ); - await embeddable.initializeSavedVis({} as LensEmbeddableInput); - const outputIndexPatterns = embeddable.getOutput().indexPatterns!; - expect(outputIndexPatterns.length).toEqual(2); - expect(outputIndexPatterns[0].id).toEqual('123'); - expect(outputIndexPatterns[1].id).toEqual('456'); - }); - - it('should re-render once on filter change', async () => { - const embeddable = new Embeddable( - { - timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, - attributeService, - data: dataMock, - uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, - expressionRenderer, - coreStart: {} as CoreStart, - basePath, - inspector: inspectorPluginMock.createStartContract(), - dataViews: {} as DataViewsContract, - capabilities: { - canSaveDashboards: true, - canSaveVisualizations: true, - discover: {}, - navLinks: {}, - }, - getTrigger, - visualizationMap: defaultVisualizationMap, - datasourceMap: defaultDatasourceMap, - injectFilterReferences: jest.fn(mockInjectFilterReferences), - theme: themeServiceMock.createStartContract(), - documentToExpression: () => - Promise.resolve({ - ast: { - type: 'expression', - chain: [ - { type: 'function', function: 'my', arguments: {} }, - { type: 'function', function: 'expression', arguments: {} }, - ], - }, - indexPatterns: {}, - indexPatternRefs: [], - }), - }, - { id: '123' } as LensEmbeddableInput - ); - await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); - embeddable.render(mountpoint); - - expect(expressionRenderer).toHaveBeenCalledTimes(1); - - embeddable.updateInput({ - filters: [{ meta: { alias: 'test', negate: false, disabled: false } }], - }); - - await new Promise((resolve) => setTimeout(resolve, 0)); - - expect(expressionRenderer).toHaveBeenCalledTimes(2); - }); - - it('should re-render once on search session change', async () => { - const embeddable = new Embeddable( - { - timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, - attributeService, - data: dataMock, - uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, - expressionRenderer, - coreStart: {} as CoreStart, - basePath, - inspector: inspectorPluginMock.createStartContract(), - dataViews: {} as DataViewsContract, - capabilities: { - canSaveDashboards: true, - canSaveVisualizations: true, - discover: {}, - navLinks: {}, - }, - getTrigger, - visualizationMap: defaultVisualizationMap, - datasourceMap: defaultDatasourceMap, - injectFilterReferences: jest.fn(mockInjectFilterReferences), - theme: themeServiceMock.createStartContract(), - documentToExpression: () => - Promise.resolve({ - ast: { - type: 'expression', - chain: [ - { type: 'function', function: 'my', arguments: {} }, - { type: 'function', function: 'expression', arguments: {} }, - ], - }, - indexPatterns: {}, - indexPatternRefs: [], - }), - }, - { id: '123', searchSessionId: 'firstSession' } as LensEmbeddableInput + { id: '123', searchSessionId: 'firstSession' } as LensEmbeddableInput ); await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); embeddable.render(mountpoint); @@ -659,43 +461,7 @@ describe('embeddable', () => { dynamicActions: {}, }, } as unknown as LensEmbeddableInput; - const embeddable = new Embeddable( - { - timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, - attributeService, - data: dataMock, - uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, - expressionRenderer, - coreStart: {} as CoreStart, - basePath, - inspector: inspectorPluginMock.createStartContract(), - dataViews: {} as DataViewsContract, - capabilities: { - canSaveDashboards: true, - canSaveVisualizations: true, - discover: {}, - navLinks: {}, - }, - getTrigger, - visualizationMap: defaultVisualizationMap, - datasourceMap: defaultDatasourceMap, - injectFilterReferences: jest.fn(mockInjectFilterReferences), - theme: themeServiceMock.createStartContract(), - documentToExpression: () => - Promise.resolve({ - ast: { - type: 'expression', - chain: [ - { type: 'function', function: 'my', arguments: {} }, - { type: 'function', function: 'expression', arguments: {} }, - ], - }, - indexPatterns: {}, - indexPatternRefs: [], - }), - }, - { id: '123' } as LensEmbeddableInput - ); + const embeddable = new Embeddable(getEmbeddableProps(), { id: '123' } as LensEmbeddableInput); await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); embeddable.render(mountpoint); @@ -716,43 +482,7 @@ describe('embeddable', () => { }); it('should re-render when dynamic actions input changes', async () => { - const embeddable = new Embeddable( - { - timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, - attributeService, - data: dataMock, - uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, - expressionRenderer, - coreStart: {} as CoreStart, - basePath, - inspector: inspectorPluginMock.createStartContract(), - dataViews: {} as DataViewsContract, - capabilities: { - canSaveDashboards: true, - canSaveVisualizations: true, - discover: {}, - navLinks: {}, - }, - getTrigger, - visualizationMap: defaultVisualizationMap, - datasourceMap: defaultDatasourceMap, - injectFilterReferences: jest.fn(mockInjectFilterReferences), - theme: themeServiceMock.createStartContract(), - documentToExpression: () => - Promise.resolve({ - ast: { - type: 'expression', - chain: [ - { type: 'function', function: 'my', arguments: {} }, - { type: 'function', function: 'expression', arguments: {} }, - ], - }, - indexPatterns: {}, - indexPatternRefs: [], - }), - }, - { id: '123' } as LensEmbeddableInput - ); + const embeddable = new Embeddable(getEmbeddableProps(), { id: '123' } as LensEmbeddableInput); await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); embeddable.render(mountpoint); @@ -780,43 +510,7 @@ describe('embeddable', () => { searchSessionId: 'searchSessionId', } as LensEmbeddableInput; - const embeddable = new Embeddable( - { - timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, - attributeService, - data: dataMock, - uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, - expressionRenderer, - coreStart: {} as CoreStart, - basePath, - inspector: inspectorPluginMock.createStartContract(), - dataViews: {} as DataViewsContract, - capabilities: { - canSaveDashboards: true, - canSaveVisualizations: true, - discover: {}, - navLinks: {}, - }, - getTrigger, - visualizationMap: defaultVisualizationMap, - datasourceMap: defaultDatasourceMap, - injectFilterReferences: jest.fn(mockInjectFilterReferences), - theme: themeServiceMock.createStartContract(), - documentToExpression: () => - Promise.resolve({ - ast: { - type: 'expression', - chain: [ - { type: 'function', function: 'my', arguments: {} }, - { type: 'function', function: 'expression', arguments: {} }, - ], - }, - indexPatterns: {}, - indexPatternRefs: [], - }), - }, - input - ); + const embeddable = new Embeddable(getEmbeddableProps(), input); await embeddable.initializeSavedVis(input); embeddable.render(mountpoint); @@ -845,43 +539,7 @@ describe('embeddable', () => { disableTriggers: true, } as LensEmbeddableInput; - const embeddable = new Embeddable( - { - timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, - attributeService, - data: dataMock, - uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, - expressionRenderer, - coreStart: {} as CoreStart, - basePath, - inspector: inspectorPluginMock.createStartContract(), - dataViews: {} as DataViewsContract, - capabilities: { - canSaveDashboards: true, - canSaveVisualizations: true, - discover: {}, - navLinks: {}, - }, - getTrigger, - visualizationMap: defaultVisualizationMap, - datasourceMap: defaultDatasourceMap, - injectFilterReferences: jest.fn(mockInjectFilterReferences), - theme: themeServiceMock.createStartContract(), - documentToExpression: () => - Promise.resolve({ - ast: { - type: 'expression', - chain: [ - { type: 'function', function: 'my', arguments: {} }, - { type: 'function', function: 'expression', arguments: {} }, - ], - }, - indexPatterns: {}, - indexPatternRefs: [], - }), - }, - input - ); + const embeddable = new Embeddable(getEmbeddableProps(), input); await embeddable.initializeSavedVis(input); embeddable.render(mountpoint); @@ -909,45 +567,11 @@ describe('embeddable', () => { }, references: [{ type: 'index-pattern', name: 'filter-0', id: 'my-index-pattern-id' }], }; - attributeService = attributeServiceMockFromSavedVis(newSavedVis); const input = { savedObjectId: '123', timeRange, query, filters } as LensEmbeddableInput; const embeddable = new Embeddable( - { - timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, - attributeService, - data: dataMock, - uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, - expressionRenderer, - coreStart: {} as CoreStart, - basePath, - inspector: inspectorPluginMock.createStartContract(), - dataViews: { get: jest.fn() } as unknown as DataViewsContract, - capabilities: { - canSaveDashboards: true, - canSaveVisualizations: true, - discover: {}, - navLinks: {}, - }, - getTrigger, - visualizationMap: defaultVisualizationMap, - datasourceMap: defaultDatasourceMap, - injectFilterReferences: jest.fn(mockInjectFilterReferences), - theme: themeServiceMock.createStartContract(), - documentToExpression: () => - Promise.resolve({ - ast: { - type: 'expression', - chain: [ - { type: 'function', function: 'my', arguments: {} }, - { type: 'function', function: 'expression', arguments: {} }, - ], - }, - indexPatterns: {}, - indexPatternRefs: [], - }), - }, + getEmbeddableProps({ attributeService: attributeServiceMockFromSavedVis(newSavedVis) }), input ); await embeddable.initializeSavedVis(input); @@ -966,43 +590,7 @@ describe('embeddable', () => { }); it('should execute trigger on event from expression renderer', async () => { - const embeddable = new Embeddable( - { - timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, - attributeService, - data: dataMock, - uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, - expressionRenderer, - coreStart: {} as CoreStart, - basePath, - inspector: inspectorPluginMock.createStartContract(), - dataViews: {} as DataViewsContract, - capabilities: { - canSaveDashboards: true, - canSaveVisualizations: true, - discover: {}, - navLinks: {}, - }, - getTrigger, - visualizationMap: defaultVisualizationMap, - datasourceMap: defaultDatasourceMap, - injectFilterReferences: jest.fn(mockInjectFilterReferences), - theme: themeServiceMock.createStartContract(), - documentToExpression: () => - Promise.resolve({ - ast: { - type: 'expression', - chain: [ - { type: 'function', function: 'my', arguments: {} }, - { type: 'function', function: 'expression', arguments: {} }, - ], - }, - indexPatterns: {}, - indexPatternRefs: [], - }), - }, - { id: '123' } as LensEmbeddableInput - ); + const embeddable = new Embeddable(getEmbeddableProps(), { id: '123' } as LensEmbeddableInput); await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); embeddable.render(mountpoint); @@ -1012,104 +600,37 @@ describe('embeddable', () => { onEvent({ name: 'brush', data: eventData }); expect(getTrigger).toHaveBeenCalledWith(VIS_EVENT_TO_TRIGGER.brush); - expect(trigger.exec).toHaveBeenCalledWith( - expect.objectContaining({ - data: { ...eventData, timeFieldName: undefined }, - embeddable: expect.anything(), - }) - ); - }); - - it('should execute trigger on row click event from expression renderer', async () => { - const embeddable = new Embeddable( - { - timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, - attributeService, - data: dataMock, - uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, - expressionRenderer, - coreStart: {} as CoreStart, - basePath, - inspector: inspectorPluginMock.createStartContract(), - dataViews: {} as DataViewsContract, - capabilities: { - canSaveDashboards: true, - canSaveVisualizations: true, - discover: {}, - navLinks: {}, - }, - getTrigger, - visualizationMap: defaultVisualizationMap, - datasourceMap: defaultDatasourceMap, - injectFilterReferences: jest.fn(mockInjectFilterReferences), - theme: themeServiceMock.createStartContract(), - documentToExpression: () => - Promise.resolve({ - ast: { - type: 'expression', - chain: [ - { type: 'function', function: 'my', arguments: {} }, - { type: 'function', function: 'expression', arguments: {} }, - ], - }, - indexPatterns: {}, - indexPatternRefs: [], - }), - }, - { id: '123' } as LensEmbeddableInput - ); - await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); - embeddable.render(mountpoint); - - const onEvent = expressionRenderer.mock.calls[0][0].onEvent!; - - onEvent({ name: 'tableRowContextMenuClick', data: {} }); - - expect(getTrigger).toHaveBeenCalledWith(VIS_EVENT_TO_TRIGGER.tableRowContextMenuClick); - }); - - it('should not re-render if only change is in disabled filter', async () => { - const timeRange: TimeRange = { from: 'now-15d', to: 'now' }; - const query: Query = { language: 'kquery', query: '' }; - const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: true } }]; - - const embeddable = new Embeddable( - { - timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, - attributeService, - data: dataMock, - uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, - expressionRenderer, - coreStart: {} as CoreStart, - basePath, - inspector: inspectorPluginMock.createStartContract(), - dataViews: {} as DataViewsContract, - capabilities: { - canSaveDashboards: true, - canSaveVisualizations: true, - discover: {}, - navLinks: {}, - }, - getTrigger, - visualizationMap: defaultVisualizationMap, - datasourceMap: defaultDatasourceMap, - injectFilterReferences: jest.fn(mockInjectFilterReferences), - theme: themeServiceMock.createStartContract(), - documentToExpression: () => - Promise.resolve({ - ast: { - type: 'expression', - chain: [ - { type: 'function', function: 'my', arguments: {} }, - { type: 'function', function: 'expression', arguments: {} }, - ], - }, - indexPatterns: {}, - indexPatternRefs: [], - }), - }, - { id: '123', timeRange, query, filters } as LensEmbeddableInput + expect(trigger.exec).toHaveBeenCalledWith( + expect.objectContaining({ + data: { ...eventData, timeFieldName: undefined }, + embeddable: expect.anything(), + }) ); + }); + + it('should execute trigger on row click event from expression renderer', async () => { + const embeddable = new Embeddable(getEmbeddableProps(), { id: '123' } as LensEmbeddableInput); + await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); + embeddable.render(mountpoint); + + const onEvent = expressionRenderer.mock.calls[0][0].onEvent!; + + onEvent({ name: 'tableRowContextMenuClick', data: {} }); + + expect(getTrigger).toHaveBeenCalledWith(VIS_EVENT_TO_TRIGGER.tableRowContextMenuClick); + }); + + it('should not re-render if only change is in disabled filter', async () => { + const timeRange: TimeRange = { from: 'now-15d', to: 'now' }; + const query: Query = { language: 'kquery', query: '' }; + const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: true } }]; + + const embeddable = new Embeddable(getEmbeddableProps(), { + id: '123', + timeRange, + query, + filters, + } as LensEmbeddableInput); await embeddable.initializeSavedVis({ id: '123', timeRange, @@ -1142,43 +663,10 @@ describe('embeddable', () => { return null; }); - const embeddable = new Embeddable( - { - timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, - attributeService, - data: dataMock, - uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, - expressionRenderer, - coreStart: {} as CoreStart, - basePath, - inspector: inspectorPluginMock.createStartContract(), - dataViews: {} as DataViewsContract, - capabilities: { - canSaveDashboards: true, - canSaveVisualizations: true, - discover: {}, - navLinks: {}, - }, - getTrigger, - visualizationMap: defaultVisualizationMap, - datasourceMap: defaultDatasourceMap, - injectFilterReferences: jest.fn(mockInjectFilterReferences), - theme: themeServiceMock.createStartContract(), - documentToExpression: () => - Promise.resolve({ - ast: { - type: 'expression', - chain: [ - { type: 'function', function: 'my', arguments: {} }, - { type: 'function', function: 'expression', arguments: {} }, - ], - }, - indexPatterns: {}, - indexPatternRefs: [], - }), - }, - { id: '123', onLoad } as unknown as LensEmbeddableInput - ); + const embeddable = new Embeddable(getEmbeddableProps({ expressionRenderer }), { + id: '123', + onLoad, + } as unknown as LensEmbeddableInput); await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); embeddable.render(mountpoint); @@ -1226,43 +714,10 @@ describe('embeddable', () => { return null; }); - const embeddable = new Embeddable( - { - timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, - attributeService, - data: dataMock, - uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, - expressionRenderer, - coreStart: {} as CoreStart, - basePath, - inspector: inspectorPluginMock.createStartContract(), - dataViews: {} as DataViewsContract, - capabilities: { - canSaveDashboards: true, - canSaveVisualizations: true, - discover: {}, - navLinks: {}, - }, - getTrigger, - visualizationMap: defaultVisualizationMap, - datasourceMap: defaultDatasourceMap, - injectFilterReferences: jest.fn(mockInjectFilterReferences), - theme: themeServiceMock.createStartContract(), - documentToExpression: () => - Promise.resolve({ - ast: { - type: 'expression', - chain: [ - { type: 'function', function: 'my', arguments: {} }, - { type: 'function', function: 'expression', arguments: {} }, - ], - }, - indexPatterns: {}, - indexPatternRefs: [], - }), - }, - { id: '123', onFilter } as unknown as LensEmbeddableInput - ); + const embeddable = new Embeddable(getEmbeddableProps({ expressionRenderer }), { + id: '123', + onFilter, + } as unknown as LensEmbeddableInput); await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); embeddable.render(mountpoint); @@ -1273,6 +728,33 @@ describe('embeddable', () => { expect(onFilter).toHaveBeenCalledTimes(1); }); + it('should prevent the onFilter trigger when calling preventDefault', async () => { + const onFilter = jest.fn(({ preventDefault }) => preventDefault()); + + expressionRenderer = jest.fn(({ onEvent }) => { + setTimeout(() => { + onEvent?.({ + name: 'filter', + data: { pings: false, table: { rows: [], columns: [] }, column: 0 }, + }); + }, 10); + + return null; + }); + + const embeddable = new Embeddable(getEmbeddableProps({ expressionRenderer }), { + id: '123', + onFilter, + } as unknown as LensEmbeddableInput); + + await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); + embeddable.render(mountpoint); + + await new Promise((resolve) => setTimeout(resolve, 20)); + + expect(getTrigger).not.toHaveBeenCalled(); + }); + it('should call onBrush event on brushing', async () => { const onBrushEnd = jest.fn(); @@ -1287,43 +769,10 @@ describe('embeddable', () => { return null; }); - const embeddable = new Embeddable( - { - timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, - attributeService, - data: dataMock, - uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, - expressionRenderer, - coreStart: {} as CoreStart, - basePath, - inspector: inspectorPluginMock.createStartContract(), - dataViews: {} as DataViewsContract, - capabilities: { - canSaveDashboards: true, - canSaveVisualizations: true, - discover: {}, - navLinks: {}, - }, - getTrigger, - visualizationMap: defaultVisualizationMap, - datasourceMap: defaultDatasourceMap, - injectFilterReferences: jest.fn(mockInjectFilterReferences), - theme: themeServiceMock.createStartContract(), - documentToExpression: () => - Promise.resolve({ - ast: { - type: 'expression', - chain: [ - { type: 'function', function: 'my', arguments: {} }, - { type: 'function', function: 'expression', arguments: {} }, - ], - }, - indexPatterns: {}, - indexPatternRefs: [], - }), - }, - { id: '123', onBrushEnd } as unknown as LensEmbeddableInput - ); + const embeddable = new Embeddable(getEmbeddableProps({ expressionRenderer }), { + id: '123', + onBrushEnd, + } as unknown as LensEmbeddableInput); await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); embeddable.render(mountpoint); @@ -1334,6 +783,33 @@ describe('embeddable', () => { expect(onBrushEnd).toHaveBeenCalledTimes(1); }); + it('should prevent the onBrush trigger when calling preventDefault', async () => { + const onBrushEnd = jest.fn(({ preventDefault }) => preventDefault()); + + expressionRenderer = jest.fn(({ onEvent }) => { + setTimeout(() => { + onEvent?.({ + name: 'brush', + data: { range: [0, 1], table: { rows: [], columns: [] }, column: 0 }, + }); + }, 10); + + return null; + }); + + const embeddable = new Embeddable(getEmbeddableProps({ expressionRenderer }), { + id: '123', + onBrushEnd, + } as unknown as LensEmbeddableInput); + + await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); + embeddable.render(mountpoint); + + await new Promise((resolve) => setTimeout(resolve, 20)); + + expect(getTrigger).not.toHaveBeenCalled(); + }); + it('should call onTableRowClick event ', async () => { const onTableRowClick = jest.fn(); @@ -1345,53 +821,44 @@ describe('embeddable', () => { return null; }); - const embeddable = new Embeddable( - { - timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, - attributeService, - data: dataMock, - uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, - expressionRenderer, - coreStart: {} as CoreStart, - basePath, - inspector: inspectorPluginMock.createStartContract(), - dataViews: {} as DataViewsContract, - capabilities: { - canSaveDashboards: true, - canSaveVisualizations: true, - discover: {}, - navLinks: {}, - }, - getTrigger, - visualizationMap: defaultVisualizationMap, - datasourceMap: defaultDatasourceMap, - injectFilterReferences: jest.fn(mockInjectFilterReferences), - theme: themeServiceMock.createStartContract(), - documentToExpression: () => - Promise.resolve({ - ast: { - type: 'expression', - chain: [ - { type: 'function', function: 'my', arguments: {} }, - { type: 'function', function: 'expression', arguments: {} }, - ], - }, - indexPatterns: {}, - indexPatternRefs: [], - }), - }, - { id: '123', onTableRowClick } as unknown as LensEmbeddableInput - ); + const embeddable = new Embeddable(getEmbeddableProps({ expressionRenderer }), { + id: '123', + onTableRowClick, + } as unknown as LensEmbeddableInput); await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); embeddable.render(mountpoint); await new Promise((resolve) => setTimeout(resolve, 20)); - expect(onTableRowClick).toHaveBeenCalledWith({ name: 'test' }); + expect(onTableRowClick).toHaveBeenCalledWith(expect.objectContaining({ name: 'test' })); expect(onTableRowClick).toHaveBeenCalledTimes(1); }); + it('should prevent onTableRowClick trigger when calling preventDefault ', async () => { + const onTableRowClick = jest.fn(({ preventDefault }) => preventDefault()); + + expressionRenderer = jest.fn(({ onEvent }) => { + setTimeout(() => { + onEvent?.({ name: 'tableRowContextMenuClick', data: { name: 'test' } }); + }, 10); + + return null; + }); + + const embeddable = new Embeddable(getEmbeddableProps({ expressionRenderer }), { + id: '123', + onTableRowClick, + } as unknown as LensEmbeddableInput); + + await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); + embeddable.render(mountpoint); + + await new Promise((resolve) => setTimeout(resolve, 20)); + + expect(getTrigger).not.toHaveBeenCalled(); + }); + it('handles edit actions ', async () => { const editedVisualizationState = { value: 'edited' }; const onEditActionMock = jest.fn().mockReturnValue(editedVisualizationState); @@ -1426,34 +893,16 @@ describe('embeddable', () => { }; const embeddable = new Embeddable( - { - timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, + getEmbeddableProps({ attributeService: attributeServiceMockFromSavedVis(visDocument), - data: dataMock, - uiSettings: { get: () => undefined } as unknown as IUiSettingsClient, - expressionRenderer, - coreStart: {} as CoreStart, - basePath, - inspector: inspectorPluginMock.createStartContract(), - dataViews: {} as DataViewsContract, - capabilities: { - canSaveDashboards: true, - canSaveVisualizations: true, - discover: {}, - navLinks: {}, - }, - getTrigger, - theme: themeServiceMock.createStartContract(), - injectFilterReferences: jest.fn(mockInjectFilterReferences), visualizationMap: { [visDocument.visualizationType as string]: { onEditAction: onEditActionMock, initialize: () => {}, } as unknown as Visualization, }, - datasourceMap: defaultDatasourceMap, documentToExpression: documentToExpressionMock, - }, + }), { id: '123' } as unknown as LensEmbeddableInput ); diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index 519db972ca855..0f8594347a34b 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -138,6 +138,12 @@ export interface LensUnwrapResult { metaInfo?: LensUnwrapMetaInfo; } +interface PreventableEvent { + preventDefault(): void; +} + +export type Simplify = { [KeyType in keyof T]: T[KeyType] } & {}; + interface LensBaseEmbeddableInput extends EmbeddableInput { filters?: Filter[]; query?: Query; @@ -148,10 +154,14 @@ interface LensBaseEmbeddableInput extends EmbeddableInput { style?: React.CSSProperties; className?: string; noPadding?: boolean; - onBrushEnd?: (data: BrushTriggerEvent['data']) => void; + onBrushEnd?: (data: Simplify) => void; onLoad?: (isLoading: boolean, adapters?: Partial) => void; - onFilter?: (data: ClickTriggerEvent['data'] | MultiClickTriggerEvent['data']) => void; - onTableRowClick?: (data: LensTableRowContextMenuEvent['data']) => void; + onFilter?: ( + data: Simplify<(ClickTriggerEvent['data'] | MultiClickTriggerEvent['data']) & PreventableEvent> + ) => void; + onTableRowClick?: ( + data: Simplify + ) => void; } export type LensByValueInput = { @@ -1102,45 +1112,68 @@ export class Embeddable return; } if (isLensBrushEvent(event)) { - this.deps.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ - data: { - ...event.data, - timeFieldName: - event.data.timeFieldName || - inferTimeField(this.deps.data.datatableUtilities, event.data), - }, - embeddable: this, - }); - + let shouldExecuteDefaultTriggers = true; if (this.input.onBrushEnd) { - this.input.onBrushEnd(event.data); + this.input.onBrushEnd({ + ...event.data, + preventDefault: () => { + shouldExecuteDefaultTriggers = false; + }, + }); + } + if (shouldExecuteDefaultTriggers) { + this.deps.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ + data: { + ...event.data, + timeFieldName: + event.data.timeFieldName || + inferTimeField(this.deps.data.datatableUtilities, event.data), + }, + embeddable: this, + }); } } if (isLensFilterEvent(event) || isLensMultiFilterEvent(event)) { - this.deps.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ - data: { - ...event.data, - timeFieldName: - event.data.timeFieldName || - inferTimeField(this.deps.data.datatableUtilities, event.data), - }, - embeddable: this, - }); + let shouldExecuteDefaultTriggers = true; if (this.input.onFilter) { - this.input.onFilter(event.data); + this.input.onFilter({ + ...event.data, + preventDefault: () => { + shouldExecuteDefaultTriggers = false; + }, + }); + } + if (shouldExecuteDefaultTriggers) { + this.deps.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ + data: { + ...event.data, + timeFieldName: + event.data.timeFieldName || + inferTimeField(this.deps.data.datatableUtilities, event.data), + }, + embeddable: this, + }); } } if (isLensTableRowContextMenuClickEvent(event)) { - this.deps.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec( - { - data: event.data, - embeddable: this, - }, - true - ); + let shouldExecuteDefaultTriggers = true; if (this.input.onTableRowClick) { - this.input.onTableRowClick(event.data as unknown as LensTableRowContextMenuEvent['data']); + this.input.onTableRowClick({ + ...event.data, + preventDefault: () => { + shouldExecuteDefaultTriggers = false; + }, + }); + } + if (shouldExecuteDefaultTriggers) { + this.deps.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec( + { + data: event.data, + embeddable: this, + }, + true + ); } } diff --git a/x-pack/plugins/lens/readme.md b/x-pack/plugins/lens/readme.md index 9fec3f154fbf3..b3768ba0b2540 100644 --- a/x-pack/plugins/lens/readme.md +++ b/x-pack/plugins/lens/readme.md @@ -98,6 +98,22 @@ Filters and query `state.filters`/`state.query` define the visualization-global The `EmbeddableComponent` also takes a set of callbacks to react to user interactions with the embedded Lens visualization to integrate the visualization with the surrounding app: `onLoad`, `onBrushEnd`, `onFilter`, `onTableRowClick`. A common pattern is to keep state in the solution app which is updated within these callbacks - re-rendering the surrounding application will change the Lens attributes passed to the component which will re-render the visualization (including re-fetching data if necessary). +#### Preventing defaults + +In some scenarios it can be useful to customize the default behaviour and avoid the default Kibana triggers for a specific action, like add a filter on a bar chart/pie slice click. For this specific requirement the `data` object returned by the `onBrushEnd`, `onFilter`, `onTableRowClick` callbacks has a special `preventDefault()` function that will prevent other registered event triggers to execute: + +```tsx + { + // custom behaviour on "filter" event + ... + // now prevent to add a filter in Kibana + data.preventDefault(); + }} +/> +``` + ## Handling data views In most cases it makes sense to have a data view saved object to use the Lens embeddable. Use the data view service to find an existing data view for a given index pattern or create a new one if it doesn't exist yet: diff --git a/x-pack/plugins/observability/public/pages/alert_details/alert_details.tsx b/x-pack/plugins/observability/public/pages/alert_details/alert_details.tsx index fd93ef9eaad63..d994669fbc6b0 100644 --- a/x-pack/plugins/observability/public/pages/alert_details/alert_details.tsx +++ b/x-pack/plugins/observability/public/pages/alert_details/alert_details.tsx @@ -75,7 +75,7 @@ export function AlertDetails() { return ; } - // Redirect to the the 404 page when the user hit the page url directly in the browser while the feature flag is off. + // Redirect to the 404 page when the user hit the page url directly in the browser while the feature flag is off. if (alert && !isAlertDetailsEnabledPerApp(alert, config)) { return ; } diff --git a/x-pack/plugins/observability/public/pages/alert_details/components/page_title.stories.tsx b/x-pack/plugins/observability/public/pages/alert_details/components/page_title.stories.tsx index 4478a9ac0bc82..72c6e221cbb5a 100644 --- a/x-pack/plugins/observability/public/pages/alert_details/components/page_title.stories.tsx +++ b/x-pack/plugins/observability/public/pages/alert_details/components/page_title.stories.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { ComponentStory } from '@storybook/react'; import { EuiPageTemplate } from '@elastic/eui'; +import { ALERT_RULE_CATEGORY } from '@kbn/rule-data-utils'; import { PageTitle as Component, PageTitleProps } from './page_title'; import { alert } from '../mock/alert'; @@ -34,5 +35,21 @@ const defaultProps = { export const PageTitle = Template.bind({}); PageTitle.args = defaultProps; + +export const PageTitleForAnomaly = Template.bind({}); +PageTitleForAnomaly.args = { + ...{ + alert: { + ...defaultProps.alert, + fields: { + ...defaultProps.alert.fields, + [ALERT_RULE_CATEGORY]: 'Anomaly', + }, + }, + }, +}; + export const PageTitleUsedWithinPageTemplate = TemplateWithPageTemplate.bind({}); -PageTitleUsedWithinPageTemplate.args = defaultProps; +PageTitleUsedWithinPageTemplate.args = { + ...defaultProps, +}; 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 09a8d2439fcc8..d988be160c48a 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 @@ -8,6 +8,7 @@ import React from 'react'; import { render } from '@testing-library/react'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { ALERT_RULE_CATEGORY } from '@kbn/rule-data-utils'; import { PageTitle, PageTitleProps } from './page_title'; import { alert } from '../mock/alert'; @@ -24,9 +25,48 @@ describe('Page Title', () => { ); }; - it('should display a title when it is passed', () => { - const { getByText } = renderComp(defaultProps); - expect(getByText(defaultProps.alert.reason)).toBeTruthy(); + it('should display Log threshold title', () => { + const { getByTestId } = renderComp(defaultProps); + + expect(getByTestId('page-title-container').children.item(0)?.textContent).toEqual( + 'Log threshold breached' + ); + }); + + it('should display Anomaly title', () => { + const props: PageTitleProps = { + alert: { + ...defaultProps.alert, + fields: { + ...defaultProps.alert.fields, + [ALERT_RULE_CATEGORY]: 'Anomaly', + }, + }, + }; + + const { getByTestId } = renderComp(props); + + expect(getByTestId('page-title-container').children.item(0)?.textContent).toEqual( + 'Anomaly detected' + ); + }); + + it('should display Inventory title', () => { + const props: PageTitleProps = { + alert: { + ...defaultProps.alert, + fields: { + ...defaultProps.alert.fields, + [ALERT_RULE_CATEGORY]: 'Inventory', + }, + }, + }; + + const { getByTestId } = renderComp(props); + + expect(getByTestId('page-title-container').children.item(0)?.textContent).toEqual( + 'Inventory threshold breached' + ); }); it('should display an active badge when active is true', async () => { 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 a602b79556e4a..41172644b9cf7 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 @@ -20,6 +20,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { ALERT_DURATION, ALERT_FLAPPING, + ALERT_RULE_CATEGORY, ALERT_STATUS_ACTIVE, ALERT_STATUS_RECOVERED, TIMESTAMP, @@ -40,7 +41,13 @@ export function PageTitle({ alert }: PageTitleProps) { return (
- {alert.reason} + @@ -53,7 +60,7 @@ export function PageTitle({ alert }: PageTitleProps) { :  @@ -72,7 +79,7 @@ export function PageTitle({ alert }: PageTitleProps) { :  @@ -91,7 +98,7 @@ export function PageTitle({ alert }: PageTitleProps) { :  diff --git a/x-pack/plugins/osquery/public/actions/actions_table.tsx b/x-pack/plugins/osquery/public/actions/actions_table.tsx index 1f8fa8db5da1f..ef256d73946e3 100644 --- a/x-pack/plugins/osquery/public/actions/actions_table.tsx +++ b/x-pack/plugins/osquery/public/actions/actions_table.tsx @@ -23,7 +23,6 @@ import { useHistory } from 'react-router-dom'; import { removeMultilines } from '../../common/utils/build_query/remove_multilines'; import { useAllLiveQueries } from './use_all_live_queries'; import type { SearchHit } from '../../common/search_strategy'; -import { Direction } from '../../common/search_strategy'; import { useRouterNavigate, useKibana } from '../common/lib/kibana'; import { usePacks } from '../packs/use_packs'; @@ -63,8 +62,6 @@ const ActionsTableComponent = () => { const { data: actionsData } = useAllLiveQueries({ activePage: pageIndex, limit: pageSize, - direction: Direction.desc, - sortField: '@timestamp', filterQuery: { exists: { field: 'user_id', diff --git a/x-pack/plugins/osquery/public/actions/use_all_live_queries.ts b/x-pack/plugins/osquery/public/actions/use_all_live_queries.ts index 78a6514ce4a3a..2f540a3eaf09c 100644 --- a/x-pack/plugins/osquery/public/actions/use_all_live_queries.ts +++ b/x-pack/plugins/osquery/public/actions/use_all_live_queries.ts @@ -17,10 +17,10 @@ import { useErrorToast } from '../common/hooks/use_error_toast'; import { Direction } from '../../common/search_strategy'; export interface UseAllLiveQueriesConfig { - activePage: number; + activePage?: number; direction?: Direction; - limit: number; - sortField: string; + limit?: number; + sortField?: string; filterQuery?: ESTermQuery | ESExistsQuery | string; skip?: boolean; alertId?: string; @@ -30,10 +30,10 @@ export interface UseAllLiveQueriesConfig { const ACTIONS_QUERY_KEY = 'actions'; export const useAllLiveQueries = ({ - activePage, + activePage = 0, direction = Direction.desc, - limit, - sortField, + limit = 100, + sortField = '@timestamp', filterQuery, skip = false, alertId, diff --git a/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_action_generator.ts b/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_action_generator.ts index 9d91d2bc5e883..360becd415646 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_action_generator.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_action_generator.ts @@ -58,6 +58,7 @@ export class EndpointActionGenerator extends BaseDataGenerator { user: { id: this.randomUser(), }, + rule: undefined, }, overrides ); diff --git a/x-pack/plugins/security_solution/common/endpoint/data_loaders/setup_fleet_for_endpoint.ts b/x-pack/plugins/security_solution/common/endpoint/data_loaders/setup_fleet_for_endpoint.ts index 02372aa25b114..5aeb64e189728 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_loaders/setup_fleet_for_endpoint.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_loaders/setup_fleet_for_endpoint.ts @@ -64,6 +64,14 @@ export const setupFleetForEndpoint = async (kbnClient: KbnClient): Promise log.error(error); throw error; } + + // Install/upgrade the endpoint package + try { + await installOrUpgradeEndpointFleetPackage(kbnClient); + } catch (error) { + log.error(error); + throw error; + } }; /** diff --git a/x-pack/plugins/security_solution/common/endpoint/index_data.ts b/x-pack/plugins/security_solution/common/endpoint/index_data.ts index 24431871c09e6..77b3135c12353 100644 --- a/x-pack/plugins/security_solution/common/endpoint/index_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/index_data.ts @@ -62,8 +62,7 @@ export async function indexHostsAndAlerts( alertsPerHost: number, fleet: boolean, options: TreeOptions = {}, - DocGenerator: typeof EndpointDocGenerator = EndpointDocGenerator, - startTransform = true + DocGenerator: typeof EndpointDocGenerator = EndpointDocGenerator ): Promise { const random = seedrandom(seed); const epmEndpointPackage = await getEndpointPackageInfo(kbnClient); @@ -97,8 +96,11 @@ export async function indexHostsAndAlerts( // Keep a map of host applied policy ids (fake) to real ingest package configs (policy record) const realPolicies: Record = {}; - await waitForMetadataTransformsReady(client); - await stopMetadataTransforms(client); + const shouldWaitForEndpointMetadataDocs = fleet; + if (shouldWaitForEndpointMetadataDocs) { + await waitForMetadataTransformsReady(client); + await stopMetadataTransforms(client); + } for (let i = 0; i < numHosts; i++) { const generator = new DocGenerator(random); @@ -126,7 +128,7 @@ export async function indexHostsAndAlerts( }); } - if (startTransform) { + if (shouldWaitForEndpointMetadataDocs) { await startMetadataTransforms( client, response.agents.map((agent) => agent.id) diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/actions.test.ts b/x-pack/plugins/security_solution/common/endpoint/schema/actions.test.ts index e9cab03724c36..5dc5059d21262 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/actions.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/actions.test.ts @@ -22,6 +22,68 @@ describe('actions schemas', () => { }).not.toThrow(); }); + it.each([true, false])('should accept withAutomatedActions param', (value) => { + expect(() => { + EndpointActionListRequestSchema.query.validate({ withAutomatedActions: value }); + }).not.toThrow(); + }); + + it('should require at least 1 alert ID', () => { + expect(() => { + EndpointActionListRequestSchema.query.validate({ alertId: [] }); + }).toThrow(); + }); + + it('should accept an alert ID if not in an array', () => { + expect(() => { + EndpointActionListRequestSchema.query.validate({ alertId: uuidv4() }); + }).not.toThrow(); + }); + + it('should not accept an alert ID if empty string', () => { + expect(() => { + EndpointActionListRequestSchema.query.validate({ alertId: '' }); + }).toThrow(); + }); + + it('should accept an alert ID in an array', () => { + expect(() => { + EndpointActionListRequestSchema.query.validate({ alertId: [uuidv4()] }); + }).not.toThrow(); + }); + + it('should not accept an alert ID if empty string in an array', () => { + expect(() => { + EndpointActionListRequestSchema.query.validate({ alertId: [''] }); + }).toThrow(); + }); + + it('should accept multiple alert IDs in an array', () => { + expect(() => { + EndpointActionListRequestSchema.query.validate({ + alertId: [uuidv4(), uuidv4(), uuidv4()], + }); + }).not.toThrow(); + }); + + it('should not accept multiple alert IDs in an array if one is an empty string', () => { + expect(() => { + EndpointActionListRequestSchema.query.validate({ + alertId: [uuidv4(), '', uuidv4()], + }); + }).toThrow(); + }); + + it('should not limit multiple alert IDs', () => { + expect(() => { + EndpointActionListRequestSchema.query.validate({ + agentIds: Array(255) + .fill(1) + .map(() => uuidv4()), + }); + }).not.toThrow(); + }); + it('should require at least 1 agent ID', () => { expect(() => { EndpointActionListRequestSchema.query.validate({ agentIds: [] }); // no agent_ids provided diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/actions.ts b/x-pack/plugins/security_solution/common/endpoint/schema/actions.ts index c6fc2237f9934..a4ab7609d75c8 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/actions.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/actions.ts @@ -151,6 +151,27 @@ export const EndpointActionListRequestSchema = { }), ]) ), + withAutomatedActions: schema.boolean({ defaultValue: true }), + alertId: schema.maybe( + schema.oneOf([ + schema.arrayOf(schema.string({ minLength: 1 }), { + minSize: 1, + validate: (alertIds) => { + if (alertIds.map((v) => v.trim()).some((v) => !v.length)) { + return 'alertIds cannot contain empty strings'; + } + }, + }), + schema.string({ + minLength: 1, + validate: (alertId) => { + if (!alertId.trim().length) { + return 'alertId cannot be an empty string'; + } + }, + }), + ]) + ), }), }; diff --git a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts index 8b8ef1ed0dda6..58c07459de441 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts @@ -132,6 +132,10 @@ export interface LogsEndpointAction { user: { id: string; }; + rule?: { + id: string; + name: string; + }; } /** @@ -188,6 +192,7 @@ export interface EndpointActionData< comment?: string; parameters?: TParameters; output?: ActionResponseOutput; + alert_id?: string[]; } export interface FleetActionResponseData { @@ -375,6 +380,9 @@ export interface ActionDetails< comment?: string; /** parameters submitted with action */ parameters?: TParameters; + alertIds?: string[]; + ruleId?: string; + ruleName?: string; } export interface ActionDetailsApiResponse< diff --git a/x-pack/plugins/security_solution/common/endpoint/utils/transforms.ts b/x-pack/plugins/security_solution/common/endpoint/utils/transforms.ts index 32a36ee7c5593..5e604f2d15e4a 100644 --- a/x-pack/plugins/security_solution/common/endpoint/utils/transforms.ts +++ b/x-pack/plugins/security_solution/common/endpoint/utils/transforms.ts @@ -16,10 +16,7 @@ import { } from '../constants'; export async function waitForMetadataTransformsReady(esClient: Client): Promise { - await waitFor( - () => areMetadataTransformsReady(esClient), - 'failed while waiting for transform to start' - ); + await waitFor(() => areMetadataTransformsReady(esClient)); } export async function stopMetadataTransforms(esClient: Client): Promise { @@ -40,7 +37,7 @@ export async function stopMetadataTransforms(esClient: Client): Promise { export async function startMetadataTransforms( esClient: Client, // agentIds to wait for - agentIds?: string[] + agentIds: string[] ): Promise { const transformIds = await getMetadataTransformIds(esClient); const currentTransformId = transformIds.find((transformId) => @@ -50,7 +47,9 @@ export async function startMetadataTransforms( transformId.startsWith(METADATA_UNITED_TRANSFORM) ); if (!currentTransformId || !unitedTransformId) { - throw new Error('failed to start metadata transforms, transforms not found'); + // eslint-disable-next-line no-console + console.warn('metadata transforms not found, skipping transform start'); + return; } try { @@ -102,8 +101,8 @@ async function areMetadataTransformsReady(esClient: Client): Promise { ); } -async function waitForCurrentMetdataDocs(esClient: Client, agentIds?: string[]) { - const query = agentIds?.length +async function waitForCurrentMetdataDocs(esClient: Client, agentIds: string[]) { + const query = agentIds.length ? { bool: { filter: [ @@ -116,12 +115,9 @@ async function waitForCurrentMetdataDocs(esClient: Client, agentIds?: string[]) }, } : { - size: 1, - query: { - match_all: {}, - }, + match_all: {}, }; - const size = agentIds?.length ? agentIds.length : 1; + const size = agentIds.length ?? 1; await waitFor( async () => ( @@ -131,16 +127,14 @@ async function waitForCurrentMetdataDocs(esClient: Client, agentIds?: string[]) size, rest_total_hits_as_int: true, }) - ).hits.total === size, - 'failed while waiting for current metadata docs to populate' + ).hits.total === size ); } async function waitFor( cb: () => Promise, - errorMessage: string, interval: number = 20000, - maxAttempts = 5 + maxAttempts = 6 ): Promise { let attempts = 0; let isReady = false; @@ -151,7 +145,7 @@ async function waitFor( attempts++; if (attempts > maxAttempts) { - throw new Error(errorMessage); + return; } } } diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel.cy.ts index e2427dc9c7218..a5442b786040f 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel.cy.ts @@ -8,7 +8,15 @@ import { DOCUMENT_DETAILS_FLYOUT_HISTORY_TAB, DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB, - DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_CONTENT, + DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_BUTTON_GROUP, + DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_BUTTON, + DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_THREAT_INTELLIGENCE_BUTTON, + DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_PREVALENCE_BUTTON, + DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_CORRELATIONS_BUTTON, + DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_CONTENT, + DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_THREAT_INTELLIGENCE_CONTENT, + DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_PREVALENCE_CONTENT, + DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_CORRELATIONS_CONTENT, DOCUMENT_DETAILS_FLYOUT_INVESTIGATIONS_TAB_CONTENT, DOCUMENT_DETAILS_FLYOUT_INVESTIGATIONS_TAB, DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB, @@ -28,6 +36,10 @@ import { openInvestigationsTab, openSessionView, openVisualizeTab, + openEntities, + openThreatIntelligence, + openPrevalence, + openCorrelations, } from '../../../tasks/document_expandable_flyout'; import { cleanKibana } from '../../../tasks/common'; import { login, visit } from '../../../tasks/login'; @@ -65,7 +77,7 @@ describe.skip('Alert details expandable flyout left panel', { testIsolation: fal cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_BUTTON_GROUP).should('be.visible'); openInsightsTab(); - cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_CONTENT).should('be.visible'); + cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_BUTTON_GROUP).should('be.visible'); openInvestigationsTab(); cy.get(DOCUMENT_DETAILS_FLYOUT_INVESTIGATIONS_TAB_CONTENT).should('be.visible'); @@ -74,24 +86,67 @@ describe.skip('Alert details expandable flyout left panel', { testIsolation: fal cy.get(DOCUMENT_DETAILS_FLYOUT_HISTORY_TAB_CONTENT).should('be.visible'); }); - it('should display a button group with 2 button in the visualize tab', () => { - openVisualizeTab(); - cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_BUTTON) - .should('be.visible') - .and('have.text', 'Session View'); - cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_GRAPH_ANALYZER_BUTTON) - .should('be.visible') - .and('have.text', 'Analyzer Graph'); + describe('visualiza tab', () => { + it('should display a button group with 2 button in the visualize tab', () => { + openVisualizeTab(); + cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_BUTTON) + .should('be.visible') + .and('have.text', 'Session View'); + cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_GRAPH_ANALYZER_BUTTON) + .should('be.visible') + .and('have.text', 'Analyzer Graph'); + }); + + it('should display content when switching buttons', () => { + openVisualizeTab(); + openSessionView(); + cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_CONTENT) + .should('be.visible') + .and('have.text', 'Session view'); + + openGraphAnalyzer(); + cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_GRAPH_ANALYZER_CONTENT).should('be.visible'); + }); }); - it('should display content when switching buttons', () => { - openVisualizeTab(); - openSessionView(); - cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_CONTENT) - .should('be.visible') - .and('have.text', 'Session view'); + describe('insights tab', () => { + it('should display a button group with 4 button in the insights tab', () => { + openInsightsTab(); + cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_BUTTON) + .should('be.visible') + .and('have.text', 'Entities'); + cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_THREAT_INTELLIGENCE_BUTTON) + .should('be.visible') + .and('have.text', 'Threat Intelligence'); + cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_PREVALENCE_BUTTON) + .should('be.visible') + .and('have.text', 'Prevalence'); + cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_CORRELATIONS_BUTTON) + .should('be.visible') + .and('have.text', 'Correlations'); + }); + + it('should display content when switching buttons', () => { + openInsightsTab(); + openEntities(); + cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_CONTENT) + .should('be.visible') + .and('have.text', 'Entities'); + + openThreatIntelligence(); + cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_THREAT_INTELLIGENCE_CONTENT) + .should('be.visible') + .and('have.text', 'Threat Intelligence'); + + openPrevalence(); + cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_PREVALENCE_CONTENT) + .should('be.visible') + .and('have.text', 'Prevalence'); - openGraphAnalyzer(); - cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_GRAPH_ANALYZER_CONTENT).should('be.visible'); + openCorrelations(); + cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_CORRELATIONS_CONTENT) + .should('be.visible') + .and('have.text', 'Correlations'); + }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts index 30d25d16c93a8..adb9d75dce78e 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts @@ -22,12 +22,19 @@ import { DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_DESCRIPTION_DETAILS, DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_DESCRIPTION_EXPAND_BUTTON, DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_REASON_DETAILS, + DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITIES_HEADER, + DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITIES_CONTENT, + DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITY_PANEL_HEADER, + DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITY_PANEL_CONTENT, + DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_VIEW_ALL_ENTITIES_BUTTON, + DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_CONTENT, } from '../../../screens/document_expandable_flyout'; import { expandFirstAlertExpandableFlyout, openOverviewTab, toggleOverviewTabDescriptionSection, toggleOverviewTabInvestigationSection, + toggleOverviewTabInsightsSection, } from '../../../tasks/document_expandable_flyout'; import { cleanKibana } from '../../../tasks/common'; import { login, visit } from '../../../tasks/login'; @@ -144,5 +151,33 @@ describe.skip( openOverviewTab(); }); }); + + describe('insights section', () => { + before(() => { + toggleOverviewTabDescriptionSection(); + toggleOverviewTabInsightsSection(); + }); + + it('should display entities section', () => { + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITIES_HEADER) + .scrollIntoView() + .should('be.visible') + .and('have.text', 'Entities'); + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITIES_CONTENT).should('be.visible'); + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITY_PANEL_HEADER).should( + 'be.visible' + ); + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITY_PANEL_CONTENT).should( + 'be.visible' + ); + }); + + it('should navigate to left panel, entities tab when view all entities is clicked', () => { + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_VIEW_ALL_ENTITIES_BUTTON) + .should('be.visible') + .click(); + cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_CONTENT).should('be.visible'); + }); + }); } ); diff --git a/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts b/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts index 145ef81e44c4a..d74a7cf8678d3 100644 --- a/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts +++ b/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts @@ -8,11 +8,19 @@ import { ANALYZER_GRAPH_TEST_ID, SESSION_VIEW_TEST_ID, + ENTITIES_DETAILS_TEST_ID, + THREAT_INTELLIGENCE_DETAILS_TEST_ID, + PREVALENCE_DETAILS_TEST_ID, + CORRELATIONS_DETAILS_TEST_ID, } from '../../public/flyout/left/components/test_ids'; import { HISTORY_TAB_CONTENT_TEST_ID, - INSIGHTS_TAB_CONTENT_TEST_ID, INVESTIGATIONS_TAB_CONTENT_TEST_ID, + INSIGHTS_TAB_BUTTON_GROUP_TEST_ID, + INSIGHTS_TAB_ENTITIES_BUTTON_TEST_ID, + INSIGHTS_TAB_THREAT_INTELLIGENCE_BUTTON_TEST_ID, + INSIGHTS_TAB_PREVALENCE_BUTTON_TEST_ID, + INSIGHTS_TAB_CORRELATIONS_BUTTON_TEST_ID, VISUALIZE_TAB_BUTTON_GROUP_TEST_ID, VISUALIZE_TAB_GRAPH_ANALYZER_BUTTON_TEST_ID, VISUALIZE_TAB_SESSION_VIEW_BUTTON_TEST_ID, @@ -57,6 +65,12 @@ import { MITRE_ATTACK_TITLE_TEST_ID, REASON_DETAILS_TEST_ID, REASON_TITLE_TEST_ID, + INSIGHTS_HEADER_TEST_ID, + ENTITIES_HEADER_TEST_ID, + ENTITIES_CONTENT_TEST_ID, + ENTITY_PANEL_HEADER_TEST_ID, + ENTITY_PANEL_CONTENT_TEST_ID, + ENTITIES_VIEW_ALL_BUTTON_TEST_ID, } from '../../public/flyout/right/components/test_ids'; import { getClassSelector, getDataTestSubjectSelector } from '../helpers/common'; @@ -91,6 +105,14 @@ export const DOCUMENT_DETAILS_FLYOUT_INVESTIGATIONS_TAB = getDataTestSubjectSele INVESTIGATIONS_TAB_TEST_ID ); export const DOCUMENT_DETAILS_FLYOUT_HISTORY_TAB = getDataTestSubjectSelector(HISTORY_TAB_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_INVESTIGATIONS_TAB_CONTENT = getDataTestSubjectSelector( + INVESTIGATIONS_TAB_CONTENT_TEST_ID +); +export const DOCUMENT_DETAILS_FLYOUT_HISTORY_TAB_CONTENT = getDataTestSubjectSelector( + HISTORY_TAB_CONTENT_TEST_ID +); + +/* Left Section - Visualize tab */ export const DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_BUTTON_GROUP = getDataTestSubjectSelector( VISUALIZE_TAB_BUTTON_GROUP_TEST_ID ); @@ -103,14 +125,31 @@ export const DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_GRAPH_ANALYZER_BUTTON = getDataTestSubjectSelector(VISUALIZE_TAB_GRAPH_ANALYZER_BUTTON_TEST_ID); export const DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_GRAPH_ANALYZER_CONTENT = getDataTestSubjectSelector(ANALYZER_GRAPH_TEST_ID); -export const DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_CONTENT = getDataTestSubjectSelector( - INSIGHTS_TAB_CONTENT_TEST_ID + +/* Left Section - Insights tab */ +export const DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_BUTTON_GROUP = getDataTestSubjectSelector( + INSIGHTS_TAB_BUTTON_GROUP_TEST_ID ); -export const DOCUMENT_DETAILS_FLYOUT_INVESTIGATIONS_TAB_CONTENT = getDataTestSubjectSelector( - INVESTIGATIONS_TAB_CONTENT_TEST_ID +export const DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_BUTTON = getDataTestSubjectSelector( + INSIGHTS_TAB_ENTITIES_BUTTON_TEST_ID ); -export const DOCUMENT_DETAILS_FLYOUT_HISTORY_TAB_CONTENT = getDataTestSubjectSelector( - HISTORY_TAB_CONTENT_TEST_ID +export const DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_CONTENT = + getDataTestSubjectSelector(ENTITIES_DETAILS_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_THREAT_INTELLIGENCE_BUTTON = + getDataTestSubjectSelector(INSIGHTS_TAB_THREAT_INTELLIGENCE_BUTTON_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_THREAT_INTELLIGENCE_CONTENT = + getDataTestSubjectSelector(THREAT_INTELLIGENCE_DETAILS_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_PREVALENCE_BUTTON = getDataTestSubjectSelector( + INSIGHTS_TAB_PREVALENCE_BUTTON_TEST_ID +); +export const DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_PREVALENCE_CONTENT = getDataTestSubjectSelector( + PREVALENCE_DETAILS_TEST_ID +); +export const DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_CORRELATIONS_BUTTON = getDataTestSubjectSelector( + INSIGHTS_TAB_CORRELATIONS_BUTTON_TEST_ID +); +export const DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_CORRELATIONS_CONTENT = getDataTestSubjectSelector( + CORRELATIONS_DETAILS_TEST_ID ); /* Overview tab */ @@ -168,6 +207,19 @@ export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_HIGHLIGHTED_FIELDS_DETAILS = export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_HIGHLIGHTED_FIELDS_GO_TO_TABLE_LINK = getDataTestSubjectSelector(HIGHLIGHTED_FIELDS_GO_TO_TABLE_LINK); +export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_SECTION_HEADER = + getDataTestSubjectSelector(INSIGHTS_HEADER_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITIES_HEADER = + getDataTestSubjectSelector(ENTITIES_HEADER_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITIES_CONTENT = + getDataTestSubjectSelector(ENTITIES_CONTENT_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_VIEW_ALL_ENTITIES_BUTTON = + getDataTestSubjectSelector(ENTITIES_VIEW_ALL_BUTTON_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITY_PANEL_HEADER = + getDataTestSubjectSelector(ENTITY_PANEL_HEADER_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITY_PANEL_CONTENT = + getDataTestSubjectSelector(ENTITY_PANEL_CONTENT_TEST_ID); + /* Table tab */ export const DOCUMENT_DETAILS_FLYOUT_TABLE_TAB_FILTER = getClassSelector('euiFieldSearch'); diff --git a/x-pack/plugins/security_solution/cypress/tasks/document_expandable_flyout.ts b/x-pack/plugins/security_solution/cypress/tasks/document_expandable_flyout.ts index a93c15d95435f..de3d6e911b96c 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/document_expandable_flyout.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/document_expandable_flyout.ts @@ -16,6 +16,7 @@ import { DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB, DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INVESTIGATION_SECTION_HEADER, DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_DESCRIPTION_SECTION_HEADER, + DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_SECTION_HEADER, DOCUMENT_DETAILS_FLYOUT_TABLE_TAB, DOCUMENT_DETAILS_FLYOUT_TABLE_TAB_CLEAR_FILTER, DOCUMENT_DETAILS_FLYOUT_TABLE_TAB_FILTER, @@ -26,6 +27,10 @@ import { DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB, DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_GRAPH_ANALYZER_BUTTON, DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_BUTTON, + DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_BUTTON, + DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_PREVALENCE_BUTTON, + DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_CORRELATIONS_BUTTON, + DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_THREAT_INTELLIGENCE_BUTTON, } from '../screens/document_expandable_flyout'; import { EXPAND_ALERT_BTN } from '../screens/alerts'; import { getClassSelector } from '../helpers/common'; @@ -63,7 +68,7 @@ export const openOverviewTab = () => cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB).scrollIntoView().should('be.visible').click(); /** - * Toggle the Overview tab description section in the document details expandable flyout right section + * Toggle the Overview tab investigation section in the document details expandable flyout right section */ export const toggleOverviewTabInvestigationSection = () => cy @@ -82,6 +87,16 @@ export const toggleOverviewTabDescriptionSection = () => .should('be.visible') .click(); +/** + * Toggle the Overview tab insights section in the document details expandable flyout right section + */ +export const toggleOverviewTabInsightsSection = () => + cy + .get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_SECTION_HEADER) + .scrollIntoView() + .should('be.visible') + .click(); + /** * Open the Table tab in the document details expandable flyout right section */ @@ -98,37 +113,83 @@ export const openJsonTab = () => * Open the Visualize tab in the document details expandable flyout left section */ export const openVisualizeTab = () => - cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB).should('be.visible').click(); + cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB).scrollIntoView().should('be.visible').click(); /** * Open the Session View under the Visualize tab in the document details expandable flyout left section */ export const openSessionView = () => - cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_BUTTON).should('be.visible').click(); + cy + .get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_BUTTON) + .scrollIntoView() + .should('be.visible') + .click(); /** * Open the Graph Analyzer under the Visuablize tab in the document details expandable flyout left section */ export const openGraphAnalyzer = () => - cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_GRAPH_ANALYZER_BUTTON).should('be.visible').click(); + cy + .get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_GRAPH_ANALYZER_BUTTON) + .scrollIntoView() + .should('be.visible') + .click(); /** * Open the Insights tab in the document details expandable flyout left section */ export const openInsightsTab = () => - cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB).should('be.visible').click(); + cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB).scrollIntoView().should('be.visible').click(); + +/** + * Open the Entities tab under the Insights tab in the document details expandable flyout left section + */ +export const openEntities = () => + cy + .get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_BUTTON) + .scrollIntoView() + .should('be.visible') + .click(); +/** + * Open the Threat intelligence tab under the Insights tab in the document details expandable flyout left section + */ +export const openThreatIntelligence = () => + cy + .get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_THREAT_INTELLIGENCE_BUTTON) + .scrollIntoView() + .should('be.visible') + .click(); +/** + * Open the Prevalence tab under the Visuablize tab in the document details expandable flyout left section + */ +export const openPrevalence = () => + cy + .get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_PREVALENCE_BUTTON) + .scrollIntoView() + .should('be.visible') + .click(); + +/** + * Open the Correlations tab under the Visuablize tab in the document details expandable flyout left section + */ +export const openCorrelations = () => + cy + .get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_CORRELATIONS_BUTTON) + .scrollIntoView() + .should('be.visible') + .click(); /** * Open the Investigations tab in the document details expandable flyout left section */ export const openInvestigationsTab = () => - cy.get(DOCUMENT_DETAILS_FLYOUT_INVESTIGATIONS_TAB).should('be.visible').click(); + cy.get(DOCUMENT_DETAILS_FLYOUT_INVESTIGATIONS_TAB).scrollIntoView().should('be.visible').click(); /** * Open the History tab in the document details expandable flyout left section */ export const openHistoryTab = () => - cy.get(DOCUMENT_DETAILS_FLYOUT_HISTORY_TAB).should('be.visible').click(); + cy.get(DOCUMENT_DETAILS_FLYOUT_HISTORY_TAB).scrollIntoView().should('be.visible').click(); /** * Filter table under the Table tab in the alert details expandable flyout right section diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/endpoint_response_actions_tab.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/endpoint_response_actions_tab.tsx new file mode 100644 index 0000000000000..19328ec107891 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/event_details/endpoint_response_actions_tab.tsx @@ -0,0 +1,98 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; +import styled from 'styled-components'; +import { EuiNotificationBadge, EuiSpacer } from '@elastic/eui'; +import { ActionsLogTable } from '../../../management/components/endpoint_response_actions_list/components/actions_log_table'; +import { useGetEndpointActionList } from '../../../management/hooks'; +import type { ExpandedEventFieldsObject, RawEventData } from './types'; +import { EventsViewType } from './event_details'; +import * as i18n from './translations'; + +import { expandDottedObject } from '../../../../common/utils/expand_dotted'; +import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features'; +import { RESPONSE_ACTION_TYPES } from '../../../../common/detection_engine/rule_response_actions/schemas/response_actions'; + +const TabContentWrapper = styled.div` + height: 100%; + position: relative; +`; + +export const useEndpointResponseActionsTab = ({ + rawEventData, +}: { + rawEventData?: RawEventData; +}) => { + const responseActionsEnabled = useIsExperimentalFeatureEnabled('endpointResponseActionsEnabled'); + + const { + data: actionList, + isFetching, + isFetched, + } = useGetEndpointActionList( + { + alertId: [rawEventData?._id ?? ''], + withAutomatedActions: true, + }, + { retry: false, enabled: rawEventData && responseActionsEnabled } + ); + + const totalItemCount = useMemo(() => actionList?.total ?? 0, [actionList]); + + const expandedEventFieldsObject = rawEventData + ? (expandDottedObject(rawEventData.fields) as ExpandedEventFieldsObject) + : undefined; + + const responseActions = useMemo( + () => expandedEventFieldsObject?.kibana?.alert?.rule?.parameters?.[0].response_actions, + [expandedEventFieldsObject] + ); + + const endpointResponseActions = useMemo( + () => + responseActions?.filter( + (responseAction) => responseAction.action_type_id === RESPONSE_ACTION_TYPES.ENDPOINT + ), + [responseActions] + ); + + if (!endpointResponseActions?.length || !rawEventData || !responseActionsEnabled) { + return; + } + + return { + id: EventsViewType.endpointView, + 'data-test-subj': 'endpointViewTab', + name: i18n.ENDPOINT_VIEW, + append: ( + + {totalItemCount} + + ), + content: ( + <> + + + {isFetched && totalItemCount ? ( + null} + items={actionList?.data || []} + loading={isFetching} + onChange={() => null} + totalItemCount={totalItemCount} + queryParams={{ withAutomatedActions: true }} + showHostNames + /> + ) : null} + + + ), + }; +}; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx index 8e7619340df73..13bf589143fe6 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx @@ -21,6 +21,8 @@ import styled from 'styled-components'; import { isEmpty } from 'lodash'; import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; +import type { RawEventData } from './types'; +import { useEndpointResponseActionsTab } from './endpoint_response_actions_tab'; import type { SearchHit } from '../../../../common/search_strategy'; import { getMitreComponentParts } from '../../../detections/mitre/get_mitre_threat_component'; import { GuidedOnboardingTourStep } from '../guided_onboarding_tour/tour_step'; @@ -30,7 +32,6 @@ import { getTourAnchor, SecurityStepId, } from '../guided_onboarding_tour/tour_config'; -import type { AlertRawEventData } from './osquery_tab'; import { useOsqueryTab } from './osquery_tab'; import { EventFieldsBrowser } from './event_fields_browser'; import { JsonView } from './json_view'; @@ -74,6 +75,7 @@ export enum EventsViewType { summaryView = 'summary-view', threatIntelView = 'threat-intel-view', osqueryView = 'osquery-results-view', + endpointView = 'endpoint-results-view', } interface Props { @@ -134,6 +136,7 @@ const RendererContainer = styled.div` const ThreatTacticContainer = styled(EuiFlexGroup)` flex-grow: 0; flex-wrap: nowrap; + & .euiFlexGroup { flex-wrap: nowrap; } @@ -425,15 +428,24 @@ const EventDetailsComponent: React.FC = ({ ); const osqueryTab = useOsqueryTab({ - rawEventData: rawEventData as AlertRawEventData, + rawEventData: rawEventData as RawEventData, ...(detailsEcsData !== null ? { ecsData: detailsEcsData } : {}), }); + const endpointResponseActionsTab = useEndpointResponseActionsTab({ + rawEventData: rawEventData as RawEventData, + }); + const tabs = useMemo(() => { - return [summaryTab, threatIntelTab, tableTab, jsonTab, osqueryTab].filter( - (tab: EventViewTab | undefined): tab is EventViewTab => !!tab - ); - }, [summaryTab, threatIntelTab, tableTab, jsonTab, osqueryTab]); + return [ + summaryTab, + threatIntelTab, + tableTab, + jsonTab, + osqueryTab, + endpointResponseActionsTab, + ].filter((tab: EventViewTab | undefined): tab is EventViewTab => !!tab); + }, [summaryTab, threatIntelTab, tableTab, jsonTab, osqueryTab, endpointResponseActionsTab]); const selectedTab = useMemo( () => tabs.find((tab) => tab.id === selectedTabId) ?? tabs[0], diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/osquery_tab.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/osquery_tab.tsx index 919d2881ff909..e82e42f78e2f6 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/osquery_tab.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/osquery_tab.tsx @@ -10,13 +10,14 @@ import React, { useMemo } from 'react'; import styled from 'styled-components'; import { FormattedMessage } from '@kbn/i18n-react'; import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; +import type { RawEventData } from './types'; import { PERMISSION_DENIED } from '../../../detection_engine/rule_response_actions/osquery/translations'; import { expandDottedObject } from '../../../../common/utils/expand_dotted'; import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features'; import { useKibana } from '../../lib/kibana'; import { EventsViewType } from './event_details'; import * as i18n from './translations'; -import type { RESPONSE_ACTION_TYPES } from '../../../../common/detection_engine/rule_response_actions/schemas/response_actions'; +import { RESPONSE_ACTION_TYPES } from '../../../../common/detection_engine/rule_response_actions/schemas/response_actions'; const TabContentWrapper = styled.div` height: 100%; @@ -24,22 +25,11 @@ const TabContentWrapper = styled.div` `; type RuleParameters = Array<{ response_actions: Array<{ - action_type_id: RESPONSE_ACTION_TYPES.OSQUERY; + action_type_id: RESPONSE_ACTION_TYPES; params: Record; }>; }>; -export interface AlertRawEventData { - _id: string; - fields: { - ['agent.id']?: string[]; - ['kibana.alert.rule.parameters']: RuleParameters; - ['kibana.alert.rule.name']: string[]; - }; - - [key: string]: unknown; -} - interface ExpandedEventFieldsObject { agent?: { id: string[]; @@ -58,7 +48,7 @@ export const useOsqueryTab = ({ rawEventData, ecsData, }: { - rawEventData?: AlertRawEventData; + rawEventData?: RawEventData; ecsData?: Ecs; }) => { const { @@ -88,35 +78,39 @@ export const useOsqueryTab = ({ [] ); - if (!osquery || !rawEventData || !responseActionsEnabled || !ecsData) { - return; - } + const shouldEarlyReturn = !rawEventData || !responseActionsEnabled || !ecsData; + const alertId = rawEventData?._id ?? ''; + + const { OsqueryResults, fetchAllLiveQueries } = osquery; + + const { data: actionsData } = fetchAllLiveQueries({ + filterQuery: { term: { alert_ids: alertId } }, + alertId, + skip: shouldEarlyReturn, + }); - const expandedEventFieldsObject = expandDottedObject( - rawEventData.fields - ) as ExpandedEventFieldsObject; + const expandedEventFieldsObject = rawEventData + ? (expandDottedObject(rawEventData.fields) as ExpandedEventFieldsObject) + : undefined; const responseActions = expandedEventFieldsObject?.kibana?.alert?.rule?.parameters?.[0].response_actions; - if (!responseActions?.length) { + const osqueryResponseActions = useMemo( + () => + responseActions?.filter( + (responseAction) => responseAction.action_type_id === RESPONSE_ACTION_TYPES.OSQUERY + ), + [responseActions] + ); + + if (!osqueryResponseActions?.length || shouldEarlyReturn) { return; } - const { OsqueryResults, fetchAllLiveQueries } = osquery; - - const alertId = rawEventData._id; - - const { data: actionsData } = fetchAllLiveQueries({ - filterQuery: { term: { alert_ids: alertId } }, - activePage: 0, - limit: 100, - sortField: '@timestamp', - alertId, - }); const actionItems = actionsData?.data.items || []; - const ruleName = expandedEventFieldsObject.kibana?.alert?.rule?.name; + const ruleName = expandedEventFieldsObject?.kibana?.alert?.rule?.name; return { id: EventsViewType.osqueryView, diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/translations.ts b/x-pack/plugins/security_solution/public/common/components/event_details/translations.ts index a781de77369c2..a0fb58551deb5 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/event_details/translations.ts @@ -70,6 +70,10 @@ export const OSQUERY_VIEW = i18n.translate('xpack.securitySolution.eventDetails. defaultMessage: 'Osquery Results', }); +export const ENDPOINT_VIEW = i18n.translate('xpack.securitySolution.eventDetails.endpointView', { + defaultMessage: 'Endpoint Results', +}); + export const FIELD = i18n.translate('xpack.securitySolution.eventDetails.field', { defaultMessage: 'Field', }); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/types.ts b/x-pack/plugins/security_solution/public/common/components/event_details/types.ts index bc76ce88aa2f6..b7e15eccc4bc9 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/event_details/types.ts @@ -5,6 +5,8 @@ * 2.0. */ +import type { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common'; +import type { RESPONSE_ACTION_TYPES } from '../../../../common/detection_engine/rule_response_actions/schemas'; import type { BrowserField } from '../../containers/source'; import type { TimelineEventsDetailsItem } from '../../../../common/search_strategy'; @@ -36,3 +38,29 @@ export interface EventSummaryField { fieldType?: string; overrideField?: string; } + +export interface RawEventData { + fields: ParsedTechnicalFields; + _id: string; +} + +export interface ExpandedEventFieldsObject { + agent?: { + id: string[]; + }; + kibana: { + alert?: { + rule?: { + parameters: RuleParameters; + name: string[]; + }; + }; + }; +} + +type RuleParameters = Array<{ + response_actions: Array<{ + action_type_id: RESPONSE_ACTION_TYPES; + params: Record; + }>; +}>; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/__snapshots__/event.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/__snapshots__/event.test.ts.snap deleted file mode 100644 index dbe4ca9818123..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/__snapshots__/event.test.ts.snap +++ /dev/null @@ -1,195 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`getEventsHistogramLensAttributes should render 1`] = ` -Object { - "description": "", - "references": Array [ - Object { - "id": "security-solution-my-test", - "name": "indexpattern-datasource-layer-0039eb0c-9a1a-4687-ae54-0f4e239bec75", - "type": "index-pattern", - }, - ], - "state": Object { - "datasourceStates": Object { - "formBased": Object { - "layers": Object { - "0039eb0c-9a1a-4687-ae54-0f4e239bec75": Object { - "columnOrder": Array [ - "34919782-4546-43a5-b668-06ac934d3acd", - "aac9d7d0-13a3-480a-892b-08207a787926", - "e09e0380-0740-4105-becc-0a4ca12e3944", - ], - "columns": Object { - "34919782-4546-43a5-b668-06ac934d3acd": Object { - "dataType": "string", - "isBucketed": true, - "label": "Top values of event.dataset", - "operationType": "terms", - "params": Object { - "missingBucket": false, - "orderBy": Object { - "columnId": "e09e0380-0740-4105-becc-0a4ca12e3944", - "type": "column", - }, - "orderDirection": "desc", - "otherBucket": true, - "parentFormat": Object { - "id": "terms", - }, - "size": 10, - }, - "scale": "ordinal", - "sourceField": "event.dataset", - }, - "aac9d7d0-13a3-480a-892b-08207a787926": Object { - "dataType": "date", - "isBucketed": true, - "label": "@timestamp", - "operationType": "date_histogram", - "params": Object { - "interval": "auto", - }, - "scale": "interval", - "sourceField": "@timestamp", - }, - "e09e0380-0740-4105-becc-0a4ca12e3944": Object { - "dataType": "number", - "isBucketed": false, - "label": "Count of records", - "operationType": "count", - "scale": "ratio", - "sourceField": "___records___", - }, - }, - "incompleteColumns": Object {}, - }, - }, - }, - }, - "filters": Array [ - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.name", - "negate": false, - "params": Object { - "query": "mockHost", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.name": "mockHost", - }, - }, - }, - Object { - "meta": Object { - "alias": "", - "disabled": false, - "key": "bool", - "negate": false, - "type": "custom", - "value": "{\\"query\\": {\\"bool\\": {\\"filter\\": [{\\"bool\\": {\\"should\\": [{\\"exists\\": {\\"field\\": \\"host.name\\"}}],\\"minimum_should_match\\": 1}}]}}}", - }, - "query": Object { - "bool": Object { - "minimum_should_match": 1, - "should": Array [ - Object { - "exists": Object { - "field": "host.name", - }, - }, - ], - }, - }, - }, - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "_index", - "negate": false, - "params": Array [ - "auditbeat-mytest-*", - ], - "type": "phrases", - }, - "query": Object { - "bool": Object { - "minimum_should_match": 1, - "should": Array [ - Object { - "match_phrase": Object { - "_index": "auditbeat-mytest-*", - }, - }, - ], - }, - }, - }, - Object { - "meta": Object { - "alias": null, - "disabled": false, - "key": "host.id", - "negate": false, - "params": Object { - "query": "123", - }, - "type": "phrase", - }, - "query": Object { - "match_phrase": Object { - "host.id": "123", - }, - }, - }, - ], - "query": Object { - "language": "kql", - "query": "host.name: *", - }, - "visualization": Object { - "axisTitlesVisibilitySettings": Object { - "x": false, - "yLeft": false, - "yRight": true, - }, - "layers": Array [ - Object { - "accessors": Array [ - "e09e0380-0740-4105-becc-0a4ca12e3944", - ], - "layerId": "0039eb0c-9a1a-4687-ae54-0f4e239bec75", - "layerType": "data", - "position": "top", - "seriesType": "bar_stacked", - "showGridlines": false, - "splitAccessor": "34919782-4546-43a5-b668-06ac934d3acd", - "xAccessor": "aac9d7d0-13a3-480a-892b-08207a787926", - }, - ], - "legend": Object { - "isVisible": true, - "legendSize": "xlarge", - "position": "left", - }, - "preferredSeriesType": "bar_stacked", - "title": "Empty XY chart", - "valueLabels": "hide", - "yLeftExtent": Object { - "mode": "full", - }, - "yRightExtent": Object { - "mode": "full", - }, - }, - }, - "title": "Events", - "visualizationType": "lnsXY", -} -`; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/event.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/event.test.ts index a9a1c3951de5a..935e2a279d673 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/event.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/event.test.ts @@ -6,6 +6,7 @@ */ import { renderHook } from '@testing-library/react-hooks'; +import { useRouteSpy } from '../../../../utils/route/use_route_spy'; import { wrapper } from '../../mocks'; import { useLensAttributes } from '../../use_lens_attributes'; @@ -35,7 +36,14 @@ jest.mock('../../../../utils/route/use_route_spy', () => ({ })); describe('getEventsHistogramLensAttributes', () => { - it('should render', () => { + it('should render query and filters for hosts events histogram', () => { + (useRouteSpy as jest.Mock).mockReturnValue([ + { + detailName: undefined, + pageName: 'hosts', + tabName: 'events', + }, + ]); const { result } = renderHook( () => useLensAttributes({ @@ -44,7 +52,432 @@ describe('getEventsHistogramLensAttributes', () => { }), { wrapper } ); + expect(result?.current?.state.query).toEqual( + expect.objectContaining({ + language: 'kql', + query: 'host.name: *', + }) + ); + + expect(result?.current?.state.filters[0]).toEqual( + expect.objectContaining({ + query: { + bool: { + minimum_should_match: 1, + should: [ + { + exists: { + field: 'host.name', + }, + }, + ], + }, + }, + }) + ); - expect(result?.current).toMatchSnapshot(); + expect(result?.current?.state.filters[1]).toEqual( + expect.objectContaining({ + query: { + bool: { + minimum_should_match: 1, + should: [ + { + match_phrase: { + _index: 'auditbeat-mytest-*', + }, + }, + ], + }, + }, + }) + ); + + expect(result?.current?.state.filters[2]).toEqual( + expect.objectContaining({ + query: { + match_phrase: { + 'host.id': '123', + }, + }, + }) + ); + }); + + it('should render query and filters for host details events histogram', () => { + const { result } = renderHook( + () => + useLensAttributes({ + getLensAttributes: getEventsHistogramLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current?.state.query).toEqual( + expect.objectContaining({ + language: 'kql', + query: 'host.name: *', + }) + ); + + expect(result?.current?.state.filters[0]).toEqual( + expect.objectContaining({ + query: { + bool: { + minimum_should_match: 1, + should: [ + { + exists: { + field: 'host.name', + }, + }, + ], + }, + }, + }) + ); + + expect(result?.current?.state.filters[1]).toEqual( + expect.objectContaining({ + query: { + bool: { + minimum_should_match: 1, + should: [ + { + match_phrase: { + _index: 'auditbeat-mytest-*', + }, + }, + ], + }, + }, + }) + ); + + expect(result?.current?.state.filters[2]).toEqual( + expect.objectContaining({ + query: { + match_phrase: { + 'host.id': '123', + }, + }, + }) + ); + }); + + it('should render attributes for network events histogram', () => { + (useRouteSpy as jest.Mock).mockReturnValue([ + { + detailName: undefined, + pageName: 'network', + tabName: 'events', + }, + ]); + const { result } = renderHook( + () => + useLensAttributes({ + getLensAttributes: getEventsHistogramLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + expect(result?.current?.state.query).toEqual( + expect.objectContaining({ + language: 'kql', + query: 'host.name: *', + }) + ); + + expect(result?.current?.state.filters[0]).toEqual( + expect.objectContaining({ + query: { + bool: { + minimum_should_match: 1, + should: [ + { + exists: { + field: 'source.ip', + }, + }, + { + exists: { + field: 'destination.ip', + }, + }, + ], + }, + }, + }) + ); + + expect(result?.current?.state.filters[1]).toEqual( + expect.objectContaining({ + query: { + bool: { + minimum_should_match: 1, + should: [ + { + match_phrase: { + _index: 'auditbeat-mytest-*', + }, + }, + ], + }, + }, + }) + ); + + expect(result?.current?.state.filters[2]).toEqual( + expect.objectContaining({ + query: { + match_phrase: { + 'host.id': '123', + }, + }, + }) + ); + }); + + it('should render attributes for network details events histogram', () => { + (useRouteSpy as jest.Mock).mockReturnValue([ + { + detailName: 'mockIp', + pageName: 'network', + tabName: 'events', + }, + ]); + const { result } = renderHook( + () => + useLensAttributes({ + getLensAttributes: getEventsHistogramLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current?.state.query).toEqual( + expect.objectContaining({ + language: 'kql', + query: 'host.name: *', + }) + ); + + expect(result?.current?.state.filters[0]).toEqual( + expect.objectContaining({ + query: { + bool: { + minimum_should_match: 1, + should: [ + { + match_phrase: { + 'source.ip': 'mockIp', + }, + }, + { + match_phrase: { + 'destination.ip': 'mockIp', + }, + }, + ], + }, + }, + }) + ); + + expect(result?.current?.state.filters[1]).toEqual( + expect.objectContaining({ + query: { + bool: { + minimum_should_match: 1, + should: [ + { + exists: { + field: 'source.ip', + }, + }, + { + exists: { + field: 'destination.ip', + }, + }, + ], + }, + }, + }) + ); + + expect(result?.current?.state.filters[2]).toEqual( + expect.objectContaining({ + query: { + bool: { + minimum_should_match: 1, + should: [ + { + match_phrase: { + _index: 'auditbeat-mytest-*', + }, + }, + ], + }, + }, + }) + ); + + expect(result?.current?.state.filters[3]).toEqual( + expect.objectContaining({ + query: { + match_phrase: { + 'host.id': '123', + }, + }, + }) + ); + }); + + it('should render attributes for users events histogram', () => { + (useRouteSpy as jest.Mock).mockReturnValue([ + { + detailName: undefined, + pageName: 'users', + tabName: 'events', + }, + ]); + const { result } = renderHook( + () => + useLensAttributes({ + getLensAttributes: getEventsHistogramLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + expect(result?.current?.state.query).toEqual( + expect.objectContaining({ + language: 'kql', + query: 'host.name: *', + }) + ); + + expect(result?.current?.state.filters[0]).toEqual( + expect.objectContaining({ + query: { + bool: { + minimum_should_match: 1, + should: [ + { + exists: { + field: 'user.name', + }, + }, + ], + }, + }, + }) + ); + + expect(result?.current?.state.filters[1]).toEqual( + expect.objectContaining({ + query: { + bool: { + minimum_should_match: 1, + should: [ + { + match_phrase: { + _index: 'auditbeat-mytest-*', + }, + }, + ], + }, + }, + }) + ); + + expect(result?.current?.state.filters[2]).toEqual( + expect.objectContaining({ + query: { + match_phrase: { + 'host.id': '123', + }, + }, + }) + ); + }); + it('should render attributes for user details events histogram', () => { + (useRouteSpy as jest.Mock).mockReturnValue([ + { + detailName: 'mockUser', + pageName: 'users', + tabName: 'events', + }, + ]); + const { result } = renderHook( + () => + useLensAttributes({ + getLensAttributes: getEventsHistogramLensAttributes, + stackByField: 'event.dataset', + }), + { wrapper } + ); + + expect(result?.current?.state.query).toEqual( + expect.objectContaining({ + language: 'kql', + query: 'host.name: *', + }) + ); + + expect(result?.current?.state.filters[0]).toEqual( + expect.objectContaining({ + query: { + match_phrase: { + 'user.name': 'mockUser', + }, + }, + }) + ); + + expect(result?.current?.state.filters[1]).toEqual( + expect.objectContaining({ + query: { + bool: { + minimum_should_match: 1, + should: [ + { + exists: { + field: 'user.name', + }, + }, + ], + }, + }, + }) + ); + + expect(result?.current?.state.filters[2]).toEqual( + expect.objectContaining({ + query: { + bool: { + minimum_should_match: 1, + should: [ + { + match_phrase: { + _index: 'auditbeat-mytest-*', + }, + }, + ], + }, + }, + }) + ); + + expect(result?.current?.state.filters[3]).toEqual( + expect.objectContaining({ + query: { + match_phrase: { + 'host.id': '123', + }, + }, + }) + ); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_area.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_area.test.ts.snap index 43bc6ff353a42..c1082249eaf09 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_area.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_area.test.ts.snap @@ -69,6 +69,28 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"query\\": {\\"bool\\": {\\"filter\\": [{\\"bool\\": {\\"should\\": [{\\"exists\\": {\\"field\\": \\"user.name\\"}}],\\"minimum_should_match\\": 1}}]}}}", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "user.name", + }, + }, + ], + }, + }, + }, Object { "meta": Object { "alias": null, diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_metric.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_metric.test.ts.snap index 6e22cc1876d6f..af6054acba3bd 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_metric.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_total_users_metric.test.ts.snap @@ -56,6 +56,28 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"query\\": {\\"bool\\": {\\"filter\\": [{\\"bool\\": {\\"should\\": [{\\"exists\\": {\\"field\\": \\"user.name\\"}}],\\"minimum_should_match\\": 1}}]}}}", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "user.name", + }, + }, + ], + }, + }, + }, Object { "meta": Object { "alias": null, diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentication_metric_failure.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentication_metric_failure.test.ts.snap index 587af046984d6..cd258182bfdcc 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentication_metric_failure.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentication_metric_failure.test.ts.snap @@ -85,6 +85,28 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"query\\": {\\"bool\\": {\\"filter\\": [{\\"bool\\": {\\"should\\": [{\\"exists\\": {\\"field\\": \\"user.name\\"}}],\\"minimum_should_match\\": 1}}]}}}", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "user.name", + }, + }, + ], + }, + }, + }, Object { "meta": Object { "alias": null, diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_area.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_area.test.ts.snap index 9216cabe9f508..1aff234b94837 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_area.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_area.test.ts.snap @@ -136,6 +136,28 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"query\\": {\\"bool\\": {\\"filter\\": [{\\"bool\\": {\\"should\\": [{\\"exists\\": {\\"field\\": \\"user.name\\"}}],\\"minimum_should_match\\": 1}}]}}}", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "user.name", + }, + }, + ], + }, + }, + }, Object { "meta": Object { "alias": null, diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_bar.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_bar.test.ts.snap index 5c814accf07c2..719fa4f10c326 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_bar.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_bar.test.ts.snap @@ -141,6 +141,28 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"query\\": {\\"bool\\": {\\"filter\\": [{\\"bool\\": {\\"should\\": [{\\"exists\\": {\\"field\\": \\"user.name\\"}}],\\"minimum_should_match\\": 1}}]}}}", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "user.name", + }, + }, + ], + }, + }, + }, Object { "meta": Object { "alias": null, diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_metric_success.test.ts.snap b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_metric_success.test.ts.snap index cf82e144bbb09..446eeb48231cf 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_metric_success.test.ts.snap +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/users/__snapshots__/kpi_user_authentications_metric_success.test.ts.snap @@ -86,6 +86,28 @@ Object { }, }, }, + Object { + "meta": Object { + "alias": "", + "disabled": false, + "key": "bool", + "negate": false, + "type": "custom", + "value": "{\\"query\\": {\\"bool\\": {\\"filter\\": [{\\"bool\\": {\\"should\\": [{\\"exists\\": {\\"field\\": \\"user.name\\"}}],\\"minimum_should_match\\": 1}}]}}}", + }, + "query": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "user.name", + }, + }, + ], + }, + }, + }, Object { "meta": Object { "alias": null, diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_embeddable.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_embeddable.tsx index 64ac28fb82d5b..4a955500fbee9 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_embeddable.tsx +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_embeddable.tsx @@ -103,6 +103,7 @@ const LensEmbeddableComponent: React.FC = ({ title: '', }); const preferredSeriesType = (attributes?.state?.visualization as XYState)?.preferredSeriesType; + // Avoid hover actions button overlaps with its chart const addHoverActionsPadding = attributes?.visualizationType !== 'lnsLegacyMetric' && attributes?.visualizationType !== 'lnsPie'; @@ -181,6 +182,7 @@ const LensEmbeddableComponent: React.FC = ({ if (!Array.isArray(e.data) || preferredSeriesType !== 'area') { return; } + // Update timerange when clicking on a dot in an area chart const [{ query }] = await createFiltersFromValueClickAction({ data: e.data, negate: e.negate, 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 d323d0b7ccbfd..33f191744101b 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 @@ -19,6 +19,16 @@ export type GetLensAttributes = ( alertsOptions?: ExtraOptions ) => LensAttributes; +export interface UseLensAttributesProps { + applyGlobalQueriesAndFilters?: boolean; + extraOptions?: ExtraOptions; + getLensAttributes?: GetLensAttributes; + lensAttributes?: LensAttributes | null; + scopeId?: SourcererScopeName; + stackByField?: string; + title?: string; +} + export interface VisualizationActionsProps { className?: string; extraActions?: Action[]; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.test.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.test.tsx index 070bd87ad6d94..509aa73629d2d 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.test.tsx @@ -10,7 +10,7 @@ import { renderHook } from '@testing-library/react-hooks'; import { getExternalAlertLensAttributes } from './lens_attributes/common/external_alert'; import { useLensAttributes } from './use_lens_attributes'; import { - hostNameExistsFilter, + fieldNameExistsFilter, getDetailsPageFilter, getIndexFilters, sourceOrDestinationIpExistsFilter, @@ -70,7 +70,7 @@ describe('useLensAttributes', () => { expect(result?.current?.state.filters).toEqual([ ...getExternalAlertLensAttributes().state.filters, ...getDetailsPageFilter('hosts', 'mockHost'), - ...hostNameExistsFilter, + ...fieldNameExistsFilter('hosts'), ...getIndexFilters(['auditbeat-*']), ...filterFromSearchBar, ]); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.tsx index 2cadf8129ce07..7276bb9d2e118 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.tsx +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.tsx @@ -7,20 +7,19 @@ import { useMemo } from 'react'; import { SecurityPageName } from '../../../../common/constants'; -import { HostsTableType } from '../../../explore/hosts/store/model'; import { NetworkRouteType } from '../../../explore/network/pages/navigation/types'; import { useSourcererDataView } from '../../containers/sourcerer'; import { useDeepEqualSelector } from '../../hooks/use_selector'; import { inputsSelectors } from '../../store'; import { SourcererScopeName } from '../../store/sourcerer/model'; import { useRouteSpy } from '../../utils/route/use_route_spy'; -import type { LensAttributes, GetLensAttributes, ExtraOptions } from './types'; +import type { LensAttributes, UseLensAttributesProps } from './types'; import { getDetailsPageFilter, sourceOrDestinationIpExistsFilter, - hostNameExistsFilter, getIndexFilters, getNetworkDetailsPageFilter, + fieldNameExistsFilter, } from './utils'; export const useLensAttributes = ({ @@ -31,15 +30,7 @@ export const useLensAttributes = ({ scopeId = SourcererScopeName.default, stackByField, title, -}: { - applyGlobalQueriesAndFilters?: boolean; - extraOptions?: ExtraOptions; - getLensAttributes?: GetLensAttributes; - lensAttributes?: LensAttributes | null; - scopeId?: SourcererScopeName; - stackByField?: string; - title?: string; -}): LensAttributes | null => { +}: UseLensAttributesProps): LensAttributes | null => { const { selectedPatterns, dataViewId, indicesExist } = useSourcererDataView(scopeId); const getGlobalQuerySelector = useMemo(() => inputsSelectors.globalQuerySelector(), []); const getGlobalFiltersQuerySelector = useMemo( @@ -51,12 +42,11 @@ export const useLensAttributes = ({ const [{ detailName, pageName, tabName }] = useRouteSpy(); const tabsFilters = useMemo(() => { - if (pageName === SecurityPageName.hosts && tabName === HostsTableType.events) { - return hostNameExistsFilter; - } - - if (pageName === SecurityPageName.network && tabName === NetworkRouteType.events) { - return sourceOrDestinationIpExistsFilter; + if (tabName === NetworkRouteType.events) { + if (pageName === SecurityPageName.network) { + return sourceOrDestinationIpExistsFilter; + } + return fieldNameExistsFilter(pageName); } return []; diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/utils.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/utils.ts index 722ffd9cfe232..a934478218111 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/utils.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/utils.ts @@ -41,31 +41,36 @@ export const getDetailsPageFilter = (pageName: string, detailName?: string): Fil : []; }; -export const hostNameExistsFilter: Filter[] = [ - { - query: { - bool: { - should: [ - { - exists: { - field: 'host.name', +export const fieldNameExistsFilter = (pageName: string): Filter[] => { + const field = pageFilterFieldMap[pageName]; + + return field && pageName + ? [ + { + query: { + bool: { + should: [ + { + exists: { + field: `${field}.name`, + }, + }, + ], + minimum_should_match: 1, }, }, - ], - minimum_should_match: 1, - }, - }, - meta: { - alias: '', - disabled: false, - key: 'bool', - negate: false, - type: 'custom', - value: - '{"query": {"bool": {"filter": [{"bool": {"should": [{"exists": {"field": "host.name"}}],"minimum_should_match": 1}}]}}}', - }, - }, -]; + meta: { + alias: '', + disabled: false, + key: 'bool', + negate: false, + type: 'custom', + value: `{"query": {"bool": {"filter": [{"bool": {"should": [{"exists": {"field": "${field}.name"}}],"minimum_should_match": 1}}]}}}`, + }, + }, + ] + : []; +}; export const getNetworkDetailsPageFilter = (ipAddress?: string): Filter[] => ipAddress 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 efa9ce4831be7..c2fb0c23c9b5b 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 @@ -172,6 +172,7 @@ export const createStartServicesMock = ( }, osquery: { OsqueryResults: jest.fn().mockReturnValue(null), + fetchAllLiveQueries: jest.fn().mockReturnValue({ data: { data: { items: [] } } }), }, triggersActionsUi, cloudExperiments, diff --git a/x-pack/plugins/security_solution/public/explore/hosts/pages/hosts.tsx b/x-pack/plugins/security_solution/public/explore/hosts/pages/hosts.tsx index 7450898501746..1476fc9ba01da 100644 --- a/x-pack/plugins/security_solution/public/explore/hosts/pages/hosts.tsx +++ b/x-pack/plugins/security_solution/public/explore/hosts/pages/hosts.tsx @@ -54,7 +54,7 @@ import { useDeepEqualSelector, useShallowEqualSelector } from '../../../common/h import { useInvalidFilterQuery } from '../../../common/hooks/use_invalid_filter_query'; import { ID } from '../containers/hosts'; import { LandingPageComponent } from '../../../common/components/landing_page'; -import { hostNameExistsFilter } from '../../../common/components/visualization_actions/utils'; +import { fieldNameExistsFilter } from '../../../common/components/visualization_actions/utils'; import { dataTableSelectors } from '../../../common/store/data_table'; import { useLicense } from '../../../common/hooks/use_license'; import { tableDefaults } from '../../../common/store/data_table/defaults'; @@ -97,6 +97,7 @@ const HostsComponent = () => { const { uiSettings } = useKibana().services; const { tabName } = useParams<{ tabName: string }>(); const tabsFilters: Filter[] = React.useMemo(() => { + const hostNameExistsFilter = fieldNameExistsFilter(SecurityPageName.hosts); if (tabName === HostsTableType.events) { return [...globalFilters, ...hostNameExistsFilter]; } diff --git a/x-pack/plugins/security_solution/public/explore/hosts/pages/hosts_tabs.tsx b/x-pack/plugins/security_solution/public/explore/hosts/pages/hosts_tabs.tsx index 9d693ab637661..3069394c002ac 100644 --- a/x-pack/plugins/security_solution/public/explore/hosts/pages/hosts_tabs.tsx +++ b/x-pack/plugins/security_solution/public/explore/hosts/pages/hosts_tabs.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React from 'react'; +import React, { useMemo } from 'react'; import { Switch } from 'react-router-dom'; import { Route } from '@kbn/shared-ux-router'; @@ -14,7 +14,7 @@ import { HostsTableType } from '../store/model'; import { AnomaliesQueryTabBody } from '../../../common/containers/anomalies/anomalies_query_tab_body'; import { AnomaliesHostTable } from '../../../common/components/ml/tables/anomalies_host_table'; import { EventsQueryTabBody } from '../../../common/components/events_tab'; -import { HOSTS_PATH } from '../../../../common/constants'; +import { HOSTS_PATH, SecurityPageName } from '../../../../common/constants'; import { HostsQueryTabBody, @@ -23,7 +23,7 @@ import { SessionsTabBody, } from './navigation'; import { TableId } from '../../../../common/types'; -import { hostNameExistsFilter } from '../../../common/components/visualization_actions/utils'; +import { fieldNameExistsFilter } from '../../../common/components/visualization_actions/utils'; export const HostsTabs = React.memo( ({ deleteQuery, filterQuery, from, indexNames, isInitializing, setQuery, to, type }) => { @@ -38,6 +38,8 @@ export const HostsTabs = React.memo( type, }; + const hostNameExistsFilter = useMemo(() => fieldNameExistsFilter(SecurityPageName.hosts), []); + return ( diff --git a/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/sessions_tab_body.tsx b/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/sessions_tab_body.tsx index 647ac95148b98..c08c123a7fdf6 100644 --- a/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/sessions_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/sessions_tab_body.tsx @@ -7,17 +7,18 @@ import React, { useMemo } from 'react'; import { TableId } from '../../../../../common/types'; +import { SecurityPageName } from '../../../../app/types'; import { SessionsView } from '../../../../common/components/sessions_viewer'; -import { hostNameExistsFilter } from '../../../../common/components/visualization_actions/utils'; +import { fieldNameExistsFilter } from '../../../../common/components/visualization_actions/utils'; import { useLicense } from '../../../../common/hooks/use_license'; import type { AlertsComponentQueryProps } from './types'; export const SessionsTabBody = React.memo((alertsProps: AlertsComponentQueryProps) => { const { pageFilters, filterQuery, ...rest } = alertsProps; - const hostPageFilters = useMemo( - () => (pageFilters != null ? [...hostNameExistsFilter, ...pageFilters] : hostNameExistsFilter), - [pageFilters] - ); + const hostPageFilters = useMemo(() => { + const hostNameExistsFilter = fieldNameExistsFilter(SecurityPageName.hosts); + return pageFilters != null ? [...hostNameExistsFilter, ...pageFilters] : hostNameExistsFilter; + }, [pageFilters]); const isEnterprisePlus = useLicense().isEnterprise(); return isEnterprisePlus ? ( diff --git a/x-pack/plugins/security_solution/public/flyout/index.tsx b/x-pack/plugins/security_solution/public/flyout/index.tsx index dec91855c2628..c69deac1030e6 100644 --- a/x-pack/plugins/security_solution/public/flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/index.tsx @@ -14,11 +14,6 @@ import type { LeftPanelProps } from './left'; import { LeftPanel, LeftPanelKey } from './left'; import { LeftPanelProvider } from './left/context'; -// TODO these should be replaced by a more dynamic solution -// see https://github.com/elastic/security-team/issues/6247 -export const RIGHT_SECTION_WIDTH = 500; -export const LEFT_SECTION_WIDTH = 1000; - /** * List of all panels that will be used within the document details expandable flyout. * This needs to be passed to the expandable flyout registeredPanels property. @@ -26,7 +21,6 @@ export const LEFT_SECTION_WIDTH = 1000; export const expandableFlyoutDocumentsPanels: ExpandableFlyoutProps['registeredPanels'] = [ { key: RightPanelKey, - width: RIGHT_SECTION_WIDTH, component: (props) => ( @@ -35,7 +29,6 @@ export const expandableFlyoutDocumentsPanels: ExpandableFlyoutProps['registeredP }, { key: LeftPanelKey, - width: LEFT_SECTION_WIDTH, component: (props) => ( diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/correlations_details.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/correlations_details.tsx new file mode 100644 index 0000000000000..e971980067183 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/left/components/correlations_details.tsx @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiText } from '@elastic/eui'; +import { CORRELATIONS_DETAILS_TEST_ID } from './test_ids'; + +export const CORRELATIONS_TAB_ID = 'correlations-details'; + +/** + * Correlations displayed in the document details expandable flyout left section under the Insights tab + */ +export const CorrelationsDetails: React.FC = () => { + return {'Correlations'}; +}; + +CorrelationsDetails.displayName = 'CorrelationsDetails'; diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/entities_details.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/entities_details.tsx new file mode 100644 index 0000000000000..2109eb145a9a8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/left/components/entities_details.tsx @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiText } from '@elastic/eui'; +import { ENTITIES_DETAILS_TEST_ID } from './test_ids'; + +export const ENTITIES_TAB_ID = 'entities-details'; + +/** + * Entities displayed in the document details expandable flyout left section under the Insights tab + */ +export const EntitiesDetails: React.FC = () => { + return {'Entities'}; +}; + +EntitiesDetails.displayName = 'EntitiesDetails'; diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details.tsx new file mode 100644 index 0000000000000..a653c500cf603 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/left/components/prevalence_details.tsx @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiText } from '@elastic/eui'; +import { PREVALENCE_DETAILS_TEST_ID } from './test_ids'; + +export const PREVALENCE_TAB_ID = 'prevalence-details'; + +/** + * Prevalence displayed in the document details expandable flyout left section under the Insights tab + */ +export const PrevalenceDetails: React.FC = () => { + return {'Prevalence'}; +}; + +PrevalenceDetails.displayName = 'PrevalenceDetails'; diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts index 4adf5de724d3b..8b2804fae3e9f 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts +++ b/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts @@ -9,3 +9,9 @@ export const ANALYZER_GRAPH_TEST_ID = 'securitySolutionDocumentDetailsFlyoutAnal export const ANALYZE_GRAPH_ERROR_TEST_ID = 'securitySolutionDocumentDetailsFlyoutAnalyzerGraphError'; export const SESSION_VIEW_TEST_ID = 'securitySolutionDocumentDetailsFlyoutSessionView'; +export const ENTITIES_DETAILS_TEST_ID = 'securitySolutionDocumentDetailsFlyoutEntitiesDetails'; +export const THREAT_INTELLIGENCE_DETAILS_TEST_ID = + 'securitySolutionDocumentDetailsFlyoutThreatIntelligenceDetails'; +export const PREVALENCE_DETAILS_TEST_ID = 'securitySolutionDocumentDetailsFlyoutPrevalenceDetails'; +export const CORRELATIONS_DETAILS_TEST_ID = + 'securitySolutionDocumentDetailsFlyoutCorrelationsDetails'; diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/threat_intelligence_details.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/threat_intelligence_details.tsx new file mode 100644 index 0000000000000..01ce8894defec --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/left/components/threat_intelligence_details.tsx @@ -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 React from 'react'; +import { EuiText } from '@elastic/eui'; +import { THREAT_INTELLIGENCE_DETAILS_TEST_ID } from './test_ids'; + +export const THREAT_INTELLIGENCE_TAB_ID = 'threat-intelligence-details'; + +/** + * Threat intelligence displayed in the document details expandable flyout left section under the Insights tab + */ +export const ThreatIntelligenceDetails: React.FC = () => { + return ( + {'Threat Intelligence'} + ); +}; + +ThreatIntelligenceDetails.displayName = 'ThreatIntelligenceDetails'; diff --git a/x-pack/plugins/security_solution/public/flyout/left/index.tsx b/x-pack/plugins/security_solution/public/flyout/left/index.tsx index 1d6ad44921938..63f13245c9bed 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/index.tsx @@ -20,6 +20,8 @@ import { useLeftPanelContext } from './context'; export type LeftPanelPaths = 'visualize' | 'insights' | 'investigation' | 'history'; export const LeftPanelKey: LeftPanelProps['key'] = 'document-details-left'; +export const LeftPanelInsightsTabPath: LeftPanelProps['path'] = ['insights']; + export interface LeftPanelProps extends FlyoutPanel { key: 'document-details-left'; path?: LeftPanelPaths[]; diff --git a/x-pack/plugins/security_solution/public/flyout/left/tabs/insights_tab.tsx b/x-pack/plugins/security_solution/public/flyout/left/tabs/insights_tab.tsx index fa27f923931ed..0f96f4b99e772 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/tabs/insights_tab.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/tabs/insights_tab.tsx @@ -5,16 +5,85 @@ * 2.0. */ -import type { FC } from 'react'; -import React, { memo } from 'react'; -import { EuiText } from '@elastic/eui'; -import { INSIGHTS_TAB_CONTENT_TEST_ID } from './test_ids'; +import React, { memo, useState } from 'react'; + +import { EuiButtonGroup, EuiSpacer } from '@elastic/eui'; +import type { EuiButtonGroupOptionProps } from '@elastic/eui/src/components/button/button_group/button_group'; +import { + INSIGHTS_TAB_BUTTON_GROUP_TEST_ID, + INSIGHTS_TAB_ENTITIES_BUTTON_TEST_ID, + INSIGHTS_TAB_THREAT_INTELLIGENCE_BUTTON_TEST_ID, + INSIGHTS_TAB_PREVALENCE_BUTTON_TEST_ID, + INSIGHTS_TAB_CORRELATIONS_BUTTON_TEST_ID, +} from './test_ids'; + +import { + INSIGHTS_BUTTONGROUP_OPTIONS, + ENTITIES_BUTTON, + THREAT_INTELLIGENCE_BUTTON, + PREVALENCE_BUTTON, + CORRELATIONS_BUTTON, +} from './translations'; +import { ENTITIES_TAB_ID, EntitiesDetails } from '../components/entities_details'; +import { + THREAT_INTELLIGENCE_TAB_ID, + ThreatIntelligenceDetails, +} from '../components/threat_intelligence_details'; +import { PREVALENCE_TAB_ID, PrevalenceDetails } from '../components/prevalence_details'; +import { CORRELATIONS_TAB_ID, CorrelationsDetails } from '../components/correlations_details'; + +const insightsButtons: EuiButtonGroupOptionProps[] = [ + { + id: ENTITIES_TAB_ID, + label: ENTITIES_BUTTON, + 'data-test-subj': INSIGHTS_TAB_ENTITIES_BUTTON_TEST_ID, + }, + { + id: THREAT_INTELLIGENCE_TAB_ID, + label: THREAT_INTELLIGENCE_BUTTON, + 'data-test-subj': INSIGHTS_TAB_THREAT_INTELLIGENCE_BUTTON_TEST_ID, + }, + { + id: PREVALENCE_TAB_ID, + label: PREVALENCE_BUTTON, + 'data-test-subj': INSIGHTS_TAB_PREVALENCE_BUTTON_TEST_ID, + }, + { + id: CORRELATIONS_TAB_ID, + label: CORRELATIONS_BUTTON, + 'data-test-subj': INSIGHTS_TAB_CORRELATIONS_BUTTON_TEST_ID, + }, +]; /** * Insights view displayed in the document details expandable flyout left section */ -export const InsightsTab: FC = memo(() => { - return {'Insights'}; +export const InsightsTab: React.FC = memo(() => { + const [activeInsightsId, setActiveInsightsId] = useState(ENTITIES_TAB_ID); + const onChangeCompressed = (optionId: string) => { + setActiveInsightsId(optionId); + }; + + return ( + <> + onChangeCompressed(id)} + buttonSize="compressed" + isFullWidth + data-test-subj={INSIGHTS_TAB_BUTTON_GROUP_TEST_ID} + /> + + {activeInsightsId === ENTITIES_TAB_ID && } + {activeInsightsId === THREAT_INTELLIGENCE_TAB_ID && } + {activeInsightsId === PREVALENCE_TAB_ID && } + {activeInsightsId === CORRELATIONS_TAB_ID && } + + ); }); InsightsTab.displayName = 'InsightsTab'; diff --git a/x-pack/plugins/security_solution/public/flyout/left/tabs/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/left/tabs/test_ids.ts index 98c4dcc28fb4c..9c2e97cfdf1ca 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/tabs/test_ids.ts +++ b/x-pack/plugins/security_solution/public/flyout/left/tabs/test_ids.ts @@ -11,8 +11,16 @@ export const VISUALIZE_TAB_SESSION_VIEW_BUTTON_TEST_ID = 'securitySolutionDocumentDetailsFlyoutVisualizeTabSessionViewButton'; export const VISUALIZE_TAB_GRAPH_ANALYZER_BUTTON_TEST_ID = 'securitySolutionDocumentDetailsFlyoutVisualizeTabGraphAnalyzerButton'; -export const INSIGHTS_TAB_CONTENT_TEST_ID = - 'securitySolutionDocumentDetailsFlyoutInsightsTabContent'; +export const INSIGHTS_TAB_BUTTON_GROUP_TEST_ID = + 'securitySolutionDocumentDetailsFlyoutInsightsTabButtonGroup'; +export const INSIGHTS_TAB_ENTITIES_BUTTON_TEST_ID = + 'securitySolutionDocumentDetailsFlyoutInsightsTabEntitiesButton'; +export const INSIGHTS_TAB_THREAT_INTELLIGENCE_BUTTON_TEST_ID = + 'securitySolutionDocumentDetailsFlyoutInsightsTabThreatIntelligenceButton'; +export const INSIGHTS_TAB_PREVALENCE_BUTTON_TEST_ID = + 'securitySolutionDocumentDetailsFlyoutInsightsTabPrevalenceButton'; +export const INSIGHTS_TAB_CORRELATIONS_BUTTON_TEST_ID = + 'securitySolutionDocumentDetailsFlyoutInsightsTabCorrelationsButton'; export const INVESTIGATIONS_TAB_CONTENT_TEST_ID = 'securitySolutionDocumentDetailsFlyoutInvestigationsTabContent'; export const HISTORY_TAB_CONTENT_TEST_ID = 'securitySolutionDocumentDetailsFlyoutHistoryTabContent'; diff --git a/x-pack/plugins/security_solution/public/flyout/left/tabs/translations.ts b/x-pack/plugins/security_solution/public/flyout/left/tabs/translations.ts index 643bbb50a5a23..d77cd21e733d1 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/tabs/translations.ts +++ b/x-pack/plugins/security_solution/public/flyout/left/tabs/translations.ts @@ -27,3 +27,38 @@ export const ANALYZER_GRAPH_BUTTON = i18n.translate( defaultMessage: 'Analyzer Graph', } ); + +export const INSIGHTS_BUTTONGROUP_OPTIONS = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.insightsOptions', + { + defaultMessage: 'Insights options', + } +); + +export const ENTITIES_BUTTON = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.entitiesButton', + { + defaultMessage: 'Entities', + } +); + +export const THREAT_INTELLIGENCE_BUTTON = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.threatIntelligenceButton', + { + defaultMessage: 'Threat Intelligence', + } +); + +export const PREVALENCE_BUTTON = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.prevalenceButton', + { + defaultMessage: 'Prevalence', + } +); + +export const CORRELATIONS_BUTTON = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.correlationsButton', + { + defaultMessage: 'Correlations', + } +); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/description.stories.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/description.stories.tsx index 5588fcbc123bc..72f59d3173edd 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/description.stories.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/description.stories.tsx @@ -8,13 +8,9 @@ import React from 'react'; import { css } from '@emotion/react'; import type { Story } from '@storybook/react'; -import { RIGHT_SECTION_WIDTH } from '../..'; import { Description } from './description'; import { RightPanelContext } from '../context'; -const PADDING = 24; -const WIDTH = RIGHT_SECTION_WIDTH - 2 * PADDING; - const ruleUuid = { category: 'kibana', field: 'kibana.alert.rule.uuid', @@ -46,7 +42,7 @@ export const RuleExpand: Story = () => {
@@ -64,7 +60,7 @@ export const RuleCollapse: Story = () => {
@@ -90,7 +86,7 @@ export const Document: Story = () => {
@@ -116,7 +112,7 @@ export const EmptyDescription: Story = () => {
@@ -131,7 +127,7 @@ export const Empty: Story = () => {
diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.test.tsx new file mode 100644 index 0000000000000..fc98b138879b7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.test.tsx @@ -0,0 +1,116 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { RightPanelContext } from '../context'; +import { + ENTITIES_HEADER_TEST_ID, + ENTITY_PANEL_TEST_ID, + ENTITIES_HOST_OVERVIEW_TEST_ID, + ENTITIES_USER_OVERVIEW_TEST_ID, +} from './test_ids'; +import { EntitiesOverview } from './entities_overview'; +import { TestProviders } from '../../../common/mock'; +import { mockGetFieldsData } from '../mocks/mock_context'; + +describe('', () => { + it('should render user and host by default', () => { + const contextValue = { + eventId: 'event id', + getFieldsData: mockGetFieldsData, + } as unknown as RightPanelContext; + + const { getByTestId, queryByText, getAllByTestId } = render( + + + + + + ); + expect(getByTestId(ENTITIES_HEADER_TEST_ID)).toHaveTextContent('Entities'); + expect(getAllByTestId(ENTITY_PANEL_TEST_ID)).toHaveLength(2); + expect(queryByText('user1')).toBeInTheDocument(); + expect(getByTestId(ENTITIES_USER_OVERVIEW_TEST_ID)).toBeInTheDocument(); + expect(queryByText('host1')).toBeInTheDocument(); + expect(getByTestId(ENTITIES_HOST_OVERVIEW_TEST_ID)).toBeInTheDocument(); + }); + + it('should only render user when host name is null', () => { + const contextValue = { + eventId: 'event id', + getFieldsData: (field: string) => (field === 'user.name' ? 'user1' : null), + } as unknown as RightPanelContext; + + const { queryByTestId, queryByText, getAllByTestId } = render( + + + + + + ); + + expect(queryByTestId(ENTITY_PANEL_TEST_ID)).toBeInTheDocument(); + expect(getAllByTestId(ENTITY_PANEL_TEST_ID)).toHaveLength(1); + expect(queryByText('user1')).toBeInTheDocument(); + expect(queryByTestId(ENTITIES_USER_OVERVIEW_TEST_ID)).toBeInTheDocument(); + }); + + it('should only render host when user name is null', () => { + const contextValue = { + eventId: 'event id', + getFieldsData: (field: string) => (field === 'host.name' ? 'host1' : null), + } as unknown as RightPanelContext; + + const { queryByTestId, queryByText, getAllByTestId } = render( + + + + + + ); + + expect(queryByTestId(ENTITY_PANEL_TEST_ID)).toBeInTheDocument(); + expect(getAllByTestId(ENTITY_PANEL_TEST_ID)).toHaveLength(1); + expect(queryByText('host1')).toBeInTheDocument(); + expect(queryByTestId(ENTITIES_HOST_OVERVIEW_TEST_ID)).toBeInTheDocument(); + }); + + it('should not render if both host name and user name are null/blank', () => { + const contextValue = { + eventId: 'event id', + getFieldsData: (field: string) => {}, + } as unknown as RightPanelContext; + + const { queryByTestId } = render( + + + + + + ); + + expect(queryByTestId(ENTITIES_HEADER_TEST_ID)).not.toBeInTheDocument(); + }); + + it('should not render if eventId is null', () => { + const contextValue = { + eventId: null, + getFieldsData: (field: string) => {}, + } as unknown as RightPanelContext; + + const { queryByTestId } = render( + + + + + + ); + + expect(queryByTestId(ENTITIES_HEADER_TEST_ID)).not.toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.tsx new file mode 100644 index 0000000000000..9d42ddef6ddec --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.tsx @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle, EuiButtonEmpty } from '@elastic/eui'; +import { useExpandableFlyoutContext } from '@kbn/expandable-flyout'; +import { useRightPanelContext } from '../context'; +import { + ENTITIES_HEADER_TEST_ID, + ENTITIES_CONTENT_TEST_ID, + ENTITIES_VIEW_ALL_BUTTON_TEST_ID, +} from './test_ids'; +import { ENTITIES_TITLE, ENTITIES_TEXT, VIEW_ALL } from './translations'; +import { EntityPanel } from './entity_panel'; +import { getField } from '../../shared/utils'; +import { HostEntityOverview } from './host_entity_overview'; +import { UserEntityOverview } from './user_entity_overview'; +import { LeftPanelKey, LeftPanelInsightsTabPath } from '../../left'; + +const USER_ICON = 'user'; +const HOST_ICON = 'storage'; + +/** + * Entities section under Insights section, overview tab. It contains a preview of host and user information. + */ +export const EntitiesOverview: React.FC = () => { + const { eventId, getFieldsData, indexName } = useRightPanelContext(); + const { openLeftPanel } = useExpandableFlyoutContext(); + const hostName = getField(getFieldsData('host.name')); + const userName = getField(getFieldsData('user.name')); + + const goToEntitiesTab = useCallback(() => { + openLeftPanel({ + id: LeftPanelKey, + path: LeftPanelInsightsTabPath, + params: { + id: eventId, + indexName, + }, + }); + }, [eventId, openLeftPanel, indexName]); + + if (!eventId || (!userName && !hostName)) { + return null; + } + + return ( + <> + +
{ENTITIES_TITLE}
+
+ + + {userName && ( + + } + /> + + )} + {hostName && ( + + } + /> + + )} + + {VIEW_ALL(ENTITIES_TEXT)} + + + + ); +}; + +EntitiesOverview.displayName = 'EntitiesOverview'; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/entity_panel.stories.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/entity_panel.stories.tsx new file mode 100644 index 0000000000000..a8e7dcf73a152 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/entity_panel.stories.tsx @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import type { Story } from '@storybook/react'; +import { EntityPanel } from './entity_panel'; + +export default { + component: EntityPanel, + title: 'Flyout/EntityPanel', +}; + +const defaultProps = { + title: 'title', + iconType: 'storage', + content: 'test content', +}; + +export const Default: Story = () => { + return ; +}; + +export const Expandable: Story = () => { + return ; +}; + +export const ExpandableDefaultOpen: Story = () => { + return ; +}; + +export const EmptyDefault: Story = () => { + return ; +}; + +export const EmptyDefaultExpanded: Story = () => { + return ; +}; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/entity_panel.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/entity_panel.test.tsx new file mode 100644 index 0000000000000..0861c3682d555 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/entity_panel.test.tsx @@ -0,0 +1,112 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { EntityPanel } from './entity_panel'; +import { + ENTITY_PANEL_TEST_ID, + ENTITY_PANEL_ICON_TEST_ID, + ENTITY_PANEL_TOGGLE_BUTTON_TEST_ID, + ENTITY_PANEL_HEADER_TEST_ID, + ENTITY_PANEL_CONTENT_TEST_ID, +} from './test_ids'; + +const defaultProps = { + title: 'test', + iconType: 'storage', + content: 'test content', +}; + +describe('', () => { + describe('panel is not expandable by default', () => { + it('should render non-expandable panel by default', () => { + const { getByTestId, queryByTestId } = render(); + + expect(getByTestId(ENTITY_PANEL_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(ENTITY_PANEL_HEADER_TEST_ID)).toHaveTextContent('test'); + expect(getByTestId(ENTITY_PANEL_CONTENT_TEST_ID)).toHaveTextContent('test content'); + + expect(queryByTestId(ENTITY_PANEL_TOGGLE_BUTTON_TEST_ID)).not.toBeInTheDocument(); + expect(getByTestId(ENTITY_PANEL_ICON_TEST_ID).firstChild).toHaveAttribute( + 'data-euiicon-type', + 'storage' + ); + }); + + it('should not render content when content is null', () => { + const { queryByTestId } = render(); + + expect(queryByTestId(ENTITY_PANEL_CONTENT_TEST_ID)).not.toBeInTheDocument(); + expect(queryByTestId(ENTITY_PANEL_TOGGLE_BUTTON_TEST_ID)).not.toBeInTheDocument(); + }); + }); + + describe('panel is expandable', () => { + it('should render panel with toggle and collapsed by default', () => { + const { getByTestId, queryByTestId } = render( + + ); + expect(getByTestId(ENTITY_PANEL_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(ENTITY_PANEL_HEADER_TEST_ID)).toHaveTextContent('test'); + expect(queryByTestId(ENTITY_PANEL_CONTENT_TEST_ID)).not.toBeInTheDocument(); + }); + + it('click toggle button should expand the panel', () => { + const { getByTestId } = render(); + + const toggle = getByTestId(ENTITY_PANEL_TOGGLE_BUTTON_TEST_ID); + expect(toggle.firstChild).toHaveAttribute('data-euiicon-type', 'arrowRight'); + toggle.click(); + + expect(getByTestId(ENTITY_PANEL_CONTENT_TEST_ID)).toHaveTextContent('test content'); + expect(toggle.firstChild).toHaveAttribute('data-euiicon-type', 'arrowDown'); + }); + + it('should not render toggle or content when content is null', () => { + const { queryByTestId } = render( + + ); + expect(queryByTestId(ENTITY_PANEL_TOGGLE_BUTTON_TEST_ID)).not.toBeInTheDocument(); + expect(queryByTestId(ENTITY_PANEL_CONTENT_TEST_ID)).not.toBeInTheDocument(); + }); + }); + + describe('panel is expandable and expanded by default', () => { + it('should render header and content', () => { + const { getByTestId } = render( + + ); + expect(getByTestId(ENTITY_PANEL_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(ENTITY_PANEL_HEADER_TEST_ID)).toHaveTextContent('test'); + expect(getByTestId(ENTITY_PANEL_CONTENT_TEST_ID)).toHaveTextContent('test content'); + expect(getByTestId(ENTITY_PANEL_TOGGLE_BUTTON_TEST_ID)).toBeInTheDocument(); + }); + + it('click toggle button should collapse the panel', () => { + const { getByTestId, queryByTestId } = render( + + ); + + const toggle = getByTestId(ENTITY_PANEL_TOGGLE_BUTTON_TEST_ID); + expect(toggle.firstChild).toHaveAttribute('data-euiicon-type', 'arrowDown'); + expect(getByTestId(ENTITY_PANEL_CONTENT_TEST_ID)).toBeInTheDocument(); + + toggle.click(); + expect(toggle.firstChild).toHaveAttribute('data-euiicon-type', 'arrowRight'); + expect(queryByTestId(ENTITY_PANEL_CONTENT_TEST_ID)).not.toBeInTheDocument(); + }); + + it('should not render content when content is null', () => { + const { queryByTestId } = render( + + ); + expect(queryByTestId(ENTITY_PANEL_TOGGLE_BUTTON_TEST_ID)).not.toBeInTheDocument(); + expect(queryByTestId(ENTITY_PANEL_CONTENT_TEST_ID)).not.toBeInTheDocument(); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/entity_panel.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/entity_panel.tsx new file mode 100644 index 0000000000000..4321939a487c3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/entity_panel.tsx @@ -0,0 +1,133 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo, useState, useCallback } from 'react'; +import { + EuiButtonIcon, + EuiSplitPanel, + EuiText, + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiPanel, +} from '@elastic/eui'; +import { + ENTITY_PANEL_TEST_ID, + ENTITY_PANEL_ICON_TEST_ID, + ENTITY_PANEL_TOGGLE_BUTTON_TEST_ID, + ENTITY_PANEL_HEADER_TEST_ID, + ENTITY_PANEL_CONTENT_TEST_ID, +} from './test_ids'; + +export interface EntityPanelProps { + /** + * String value of the title to be displayed in the header of panel + */ + title: string; + /** + * Icon string for displaying the specified icon in the header + */ + iconType: string; + /** + * Content to show in the content section of the panel + */ + content?: string | React.ReactNode; + /** + * Boolean to determine the panel to be collapsable (with toggle) + */ + expandable?: boolean; + /** + * Boolean to allow the component to be expanded or collapsed on first render + */ + expanded?: boolean; +} + +/** + * Panel component to display user or host information. + */ +export const EntityPanel: React.FC = ({ + title, + iconType, + content, + expandable = false, + expanded = false, +}) => { + const [toggleStatus, setToggleStatus] = useState(expanded); + const toggleQuery = useCallback(() => { + setToggleStatus(!toggleStatus); + }, [setToggleStatus, toggleStatus]); + + const toggleIcon = useMemo( + () => ( + + + + ), + [toggleStatus, toggleQuery] + ); + + const icon = useMemo(() => { + return ( + + ); + }, [iconType]); + + const showContent = useMemo(() => { + if (!content) { + return false; + } + return !expandable || (expandable && toggleStatus); + }, [content, expandable, toggleStatus]); + + const panelHeader = useMemo(() => { + return ( + + {expandable && content && toggleIcon} + {icon} + + + {title} + + + + ); + }, [title, icon, content, toggleIcon, expandable]); + + return ( + + + {panelHeader} + + {showContent && ( + + {content} + + )} + + ); +}; + +EntityPanel.displayName = 'EntityPanel'; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/host_entity_overview.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/host_entity_overview.test.tsx new file mode 100644 index 0000000000000..b3a6a1043c030 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/host_entity_overview.test.tsx @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { render } from '@testing-library/react'; +import { TestProviders } from '../../../common/mock'; +import { HostEntityOverview } from './host_entity_overview'; +import { useRiskScore } from '../../../explore/containers/risk_score'; +import { useHostDetails } from '../../../explore/hosts/containers/hosts/details'; +import { + ENTITIES_HOST_OVERVIEW_IP_TEST_ID, + ENTITIES_HOST_OVERVIEW_RISK_LEVEL_TEST_ID, + TECHNICAL_PREVIEW_ICON_TEST_ID, +} from './test_ids'; + +const hostName = 'host'; +const ip = '10.200.000.000'; +const from = '2022-04-05T12:00:00.000Z'; +const to = '2022-04-08T12:00:00.;000Z'; +const selectedPatterns = 'alerts'; +const hostData = { host: { ip: [ip] } }; +const riskLevel = [{ host: { risk: { calculated_level: 'Medium' } } }]; + +const mockUseGlobalTime = jest.fn().mockReturnValue({ from, to }); +jest.mock('../../../common/containers/use_global_time', () => { + return { + useGlobalTime: (...props: unknown[]) => mockUseGlobalTime(...props), + }; +}); + +const mockUseSourcererDataView = jest.fn().mockReturnValue({ selectedPatterns }); +jest.mock('../../../common/containers/sourcerer', () => { + return { + useSourcererDataView: (...props: unknown[]) => mockUseSourcererDataView(...props), + }; +}); + +const mockUseHostDetails = useHostDetails as jest.Mock; +jest.mock('../../../explore/hosts/containers/hosts/details'); + +const mockUseRiskScore = useRiskScore as jest.Mock; +jest.mock('../../../explore/containers/risk_score'); + +describe('', () => { + describe('license is valid', () => { + it('should render ip addresses and host risk classification', () => { + mockUseHostDetails.mockReturnValue([false, { hostDetails: hostData }]); + mockUseRiskScore.mockReturnValue({ data: riskLevel, isLicenseValid: true }); + + const { getByTestId } = render( + + + + ); + + expect(getByTestId(ENTITIES_HOST_OVERVIEW_IP_TEST_ID)).toHaveTextContent(ip); + expect(getByTestId(TECHNICAL_PREVIEW_ICON_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(ENTITIES_HOST_OVERVIEW_RISK_LEVEL_TEST_ID)).toHaveTextContent('Medium'); + }); + + it('should render correctly if returned data is null', () => { + mockUseHostDetails.mockReturnValue([false, { hostDetails: null }]); + mockUseRiskScore.mockReturnValue({ data: null, isLicenseValid: true }); + + const { getByTestId } = render( + + + + ); + expect(getByTestId(ENTITIES_HOST_OVERVIEW_IP_TEST_ID)).toHaveTextContent('—'); + expect(getByTestId(TECHNICAL_PREVIEW_ICON_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(ENTITIES_HOST_OVERVIEW_RISK_LEVEL_TEST_ID)).toHaveTextContent('Unknown'); + }); + }); + + describe('license is not valid', () => { + it('should render ip but not host risk classification', () => { + mockUseHostDetails.mockReturnValue([false, { hostDetails: hostData }]); + mockUseRiskScore.mockReturnValue({ data: riskLevel, isLicenseValid: false }); + const { getByTestId, queryByTestId } = render( + + + + ); + + expect(getByTestId(ENTITIES_HOST_OVERVIEW_IP_TEST_ID)).toHaveTextContent(ip); + expect(queryByTestId(ENTITIES_HOST_OVERVIEW_RISK_LEVEL_TEST_ID)).not.toBeInTheDocument(); + }); + + it('should render correctly if returned data is null', () => { + mockUseHostDetails.mockReturnValue([false, { hostDetails: null }]); + mockUseRiskScore.mockReturnValue({ data: null, isLicenseValid: false }); + const { getByTestId, queryByTestId } = render( + + + + ); + + expect(getByTestId(ENTITIES_HOST_OVERVIEW_IP_TEST_ID)).toHaveTextContent('—'); + expect(queryByTestId(TECHNICAL_PREVIEW_ICON_TEST_ID)).not.toBeInTheDocument(); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/host_entity_overview.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/host_entity_overview.tsx new file mode 100644 index 0000000000000..c7b3484f19086 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/host_entity_overview.tsx @@ -0,0 +1,152 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiBetaBadge } from '@elastic/eui'; +import { getOr } from 'lodash/fp'; +import styled from 'styled-components'; +import type { DescriptionList } from '../../../../common/utility_types'; +import { + buildHostNamesFilter, + RiskScoreEntity, + RiskSeverity, +} from '../../../../common/search_strategy'; +import { DefaultFieldRenderer } from '../../../timelines/components/field_renderers/field_renderers'; +import { NetworkDetailsLink } from '../../../common/components/links'; +import { DescriptionListStyled } from '../../../common/components/page'; +import { OverviewDescriptionList } from '../../../common/components/overview_description_list'; +import { RiskScore } from '../../../explore/components/risk_score/severity/common'; +import { getEmptyTagValue } from '../../../common/components/empty_value'; +import { useSourcererDataView } from '../../../common/containers/sourcerer'; +import { useGlobalTime } from '../../../common/containers/use_global_time'; +import { useRiskScore } from '../../../explore/containers/risk_score'; +import { useHostDetails } from '../../../explore/hosts/containers/hosts/details'; +import * as i18n from '../../../overview/components/host_overview/translations'; +import { TECHNICAL_PREVIEW_TITLE, TECHNICAL_PREVIEW_MESSAGE } from './translations'; +import { + TECHNICAL_PREVIEW_ICON_TEST_ID, + ENTITIES_HOST_OVERVIEW_TEST_ID, + ENTITIES_HOST_OVERVIEW_IP_TEST_ID, + ENTITIES_HOST_OVERVIEW_RISK_LEVEL_TEST_ID, +} from './test_ids'; + +const StyledEuiBetaBadge = styled(EuiBetaBadge)` + margin-left: ${({ theme }) => theme.eui.euiSizeXS}; +`; +const CONTEXT_ID = `flyout-host-entity-overview`; + +export interface HostEntityOverviewProps { + /** + * Host name for looking up host related ip addresses and risk classification + */ + hostName: string; +} + +/** + * Host preview content for the entities preview in right flyout. It contains ip addresses and risk classification + */ +export const HostEntityOverview: React.FC = ({ hostName }) => { + const { from, to } = useGlobalTime(); + const { selectedPatterns } = useSourcererDataView(); + + const timerange = useMemo( + () => ({ + from, + to, + }), + [from, to] + ); + + const filterQuery = useMemo( + () => (hostName ? buildHostNamesFilter([hostName]) : undefined), + [hostName] + ); + + const { data: hostRisk, isLicenseValid } = useRiskScore({ + filterQuery, + riskEntity: RiskScoreEntity.host, + skip: hostName == null, + timerange, + }); + + const [_, { hostDetails }] = useHostDetails({ + hostName, + indexNames: selectedPatterns, + startDate: from, + endDate: to, + }); + + const [hostRiskLevel] = useMemo(() => { + const hostRiskData = hostRisk && hostRisk.length > 0 ? hostRisk[0] : undefined; + return [ + { + title: ( + <> + {i18n.HOST_RISK_CLASSIFICATION} + + + ), + description: ( + <> + {hostRiskData ? ( + + ) : ( + + )} + + ), + }, + ]; + }, [hostRisk]); + + const descriptionList: DescriptionList[] = useMemo( + () => [ + { + title: i18n.IP_ADDRESSES, + description: ( + (ip != null ? : getEmptyTagValue())} + /> + ), + }, + ], + [hostDetails] + ); + + return ( + + + + + + {isLicenseValid && ( + + )} + + + ); +}; + +HostEntityOverview.displayName = 'HostEntityOverview'; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_section.stories.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_section.stories.tsx new file mode 100644 index 0000000000000..953edc6d57f99 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_section.stories.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 from 'react'; +import type { Story } from '@storybook/react'; +import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context'; +import { InsightsSection } from './insights_section'; +import { RightPanelContext } from '../context'; + +const flyoutContextValue = { + openLeftPanel: () => window.alert('openLeftPanel'), +} as unknown as ExpandableFlyoutContext; +const panelContextValue = { + getFieldsData: () => ({ + host: { + name: 'hostName', + }, + user: { + name: 'userName', + }, + }), +} as unknown as RightPanelContext; + +export default { + component: InsightsSection, + title: 'Flyout/InsightsSection', +}; + +export const Expand: Story = () => { + return ( + + + + + + ); +}; + +export const Collapse: Story = () => { + return ( + + + + + + ); +}; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_section.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_section.test.tsx new file mode 100644 index 0000000000000..3db91442ee94e --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_section.test.tsx @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { RightPanelContext } from '../context'; +import { INSIGHTS_HEADER_TEST_ID } from './test_ids'; +import { TestProviders } from '../../../common/mock'; +import { mockGetFieldsData } from '../mocks/mock_context'; +import { InsightsSection } from './insights_section'; + +const mockDispatch = jest.fn(); +jest.mock('react-redux', () => { + const original = jest.requireActual('react-redux'); + + return { + ...original, + useDispatch: () => mockDispatch, + }; +}); + +describe('', () => { + it('should render insights component', () => { + const contextValue = { + eventId: 'some_Id', + getFieldsData: mockGetFieldsData, + } as unknown as RightPanelContext; + + const wrapper = render( + + + + + + ); + + expect(wrapper.getByTestId(INSIGHTS_HEADER_TEST_ID)).toBeInTheDocument(); + expect(wrapper.getAllByRole('button')[0]).toHaveAttribute('aria-expanded', 'false'); + expect(wrapper.getAllByRole('button')[0]).not.toHaveAttribute('disabled'); + }); + + it('should render insights component as expanded when expanded is true', () => { + const contextValue = { + eventId: 'some_Id', + getFieldsData: mockGetFieldsData, + } as unknown as RightPanelContext; + + const wrapper = render( + + + + + + ); + + expect(wrapper.getByTestId(INSIGHTS_HEADER_TEST_ID)).toBeInTheDocument(); + expect(wrapper.getAllByRole('button')[0]).toHaveAttribute('aria-expanded', 'true'); + expect(wrapper.getAllByRole('button')[0]).not.toHaveAttribute('disabled'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_section.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_section.tsx new file mode 100644 index 0000000000000..8409676b610b0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_section.tsx @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { INSIGHTS_TEST_ID } from './test_ids'; +import { INSIGHTS_TITLE } from './translations'; +import { EntitiesOverview } from './entities_overview'; +import { ExpandableSection } from './expandable_section'; + +export interface InsightsSectionProps { + /** + * Boolean to allow the component to be expanded or collapsed on first render + */ + expanded?: boolean; +} + +/** + * Insights section under overview tab. It contains entities, threat intelligence, prevalence and correlations. + */ +export const InsightsSection: React.FC = ({ expanded = false }) => { + return ( + + + + ); +}; + +InsightsSection.displayName = 'InsightsSection'; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/investigation_section.stories.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/investigation_section.stories.tsx index 48e763aefaca9..e7e1bc59b579a 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/investigation_section.stories.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/investigation_section.stories.tsx @@ -27,7 +27,7 @@ export const Expand: Story = () => { return ( - + ); @@ -37,7 +37,7 @@ export const Collapse: Story = () => { return ( - + ); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts index a18a127cafb51..65850272b2289 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts +++ b/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts @@ -52,3 +52,31 @@ export const HIGHLIGHTED_FIELDS_DETAILS_TEST_ID = export const HIGHLIGHTED_FIELDS_TEST_ID = 'securitySolutionDocumentDetailsFlyoutHighlightedFields'; export const HIGHLIGHTED_FIELDS_HEADER_EXPAND_ICON_TEST_ID = 'query-toggle-header'; export const HIGHLIGHTED_FIELDS_GO_TO_TABLE_LINK = 'summary-view-go-to-table-link'; +export const INSIGHTS_TEST_ID = 'securitySolutionDocumentDetailsFlyoutInsights'; +export const INSIGHTS_HEADER_TEST_ID = 'securitySolutionDocumentDetailsFlyoutInsightsHeader'; +export const ENTITIES_HEADER_TEST_ID = 'securitySolutionDocumentDetailsFlyoutEntitiesHeader'; +export const ENTITIES_CONTENT_TEST_ID = 'securitySolutionDocumentDetailsFlyoutEntitiesContent'; +export const ENTITIES_VIEW_ALL_BUTTON_TEST_ID = + 'securitySolutionDocumentDetailsFlyoutEntitiesViewAllButton'; +export const ENTITY_PANEL_TEST_ID = 'securitySolutionDocumentDetailsFlyoutEntityPanel'; +export const ENTITY_PANEL_ICON_TEST_ID = 'securitySolutionDocumentDetailsFlyoutEntityPanelTypeIcon'; +export const ENTITY_PANEL_TOGGLE_BUTTON_TEST_ID = + 'securitySolutionDocumentDetailsFlyoutEntityPanelToggleButton'; +export const ENTITY_PANEL_HEADER_TEST_ID = + 'securitySolutionDocumentDetailsFlyoutEntityPanelHeaderTitle'; +export const ENTITY_PANEL_CONTENT_TEST_ID = + 'securitySolutionDocumentDetailsFlyoutEntityPanelContent'; +export const TECHNICAL_PREVIEW_ICON_TEST_ID = + 'securitySolutionDocumentDetailsFlyoutTechnicalPreviewIcon'; +export const ENTITIES_USER_OVERVIEW_TEST_ID = + 'securitySolutionDocumentDetailsFlyoutEntitiesUserOverview'; +export const ENTITIES_USER_OVERVIEW_IP_TEST_ID = + 'securitySolutionDocumentDetailsFlyoutEntitiesUserOverviewIP'; +export const ENTITIES_USER_OVERVIEW_RISK_LEVEL_TEST_ID = + 'securitySolutionDocumentDetailsFlyoutEntitiesUserOverviewRiskLevel'; +export const ENTITIES_HOST_OVERVIEW_TEST_ID = + 'securitySolutionDocumentDetailsFlyoutEntitiesHostOverview'; +export const ENTITIES_HOST_OVERVIEW_IP_TEST_ID = + 'securitySolutionDocumentDetailsFlyoutEntitiesHostOverviewIP'; +export const ENTITIES_HOST_OVERVIEW_RISK_LEVEL_TEST_ID = + 'securitySolutionDocumentDetailsFlyoutEntitiesHostOverviewRiskLevel'; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts b/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts index 51461459c9cc8..169299e895e24 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts +++ b/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts @@ -100,3 +100,39 @@ export const HIGHLIGHTED_FIELDS_TITLE = i18n.translate( 'xpack.securitySolution.flyout.documentDetails.highlightedFieldsTitle', { defaultMessage: 'Highlighted fields' } ); + +export const ENTITIES_TITLE = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.entitiesTitle', + { defaultMessage: 'Entities' } +); + +export const INSIGHTS_TITLE = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.insightsTitle', + { defaultMessage: 'Insights' } +); + +export const TECHNICAL_PREVIEW_TITLE = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.technicalPreviewTitle', + { defaultMessage: 'Technical Preview' } +); + +export const TECHNICAL_PREVIEW_MESSAGE = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.technicalPreviewMessage', + { + defaultMessage: + 'This functionality is in technical preview and may be changed or removed completely in a future release. Elastic will take a best effort approach to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.', + } +); + +export const ENTITIES_TEXT = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.overviewTab.entitiesText', + { + defaultMessage: 'entities', + } +); + +export const VIEW_ALL = (text: string) => + i18n.translate('xpack.securitySolution.flyout.documentDetails.overviewTab.viewAllButton', { + values: { text }, + defaultMessage: 'View all {text}', + }); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/user_entity_overview.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/user_entity_overview.test.tsx new file mode 100644 index 0000000000000..b868d1161a65b --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/user_entity_overview.test.tsx @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { render } from '@testing-library/react'; +import { TestProviders } from '../../../common/mock'; +import { UserEntityOverview } from './user_entity_overview'; +import { useRiskScore } from '../../../explore/containers/risk_score'; +import { useUserDetails } from '../../../explore/users/containers/users/details'; +import { + ENTITIES_USER_OVERVIEW_IP_TEST_ID, + ENTITIES_USER_OVERVIEW_RISK_LEVEL_TEST_ID, + TECHNICAL_PREVIEW_ICON_TEST_ID, +} from './test_ids'; + +const userName = 'user'; +const ip = '10.200.000.000'; +const from = '2022-04-05T12:00:00.000Z'; +const to = '2022-04-08T12:00:00.;000Z'; +const selectedPatterns = 'alerts'; +const userData = { host: { ip: [ip] } }; +const riskLevel = [{ user: { risk: { calculated_level: 'Medium' } } }]; + +const mockUseGlobalTime = jest.fn().mockReturnValue({ from, to }); +jest.mock('../../../common/containers/use_global_time', () => { + return { + useGlobalTime: (...props: unknown[]) => mockUseGlobalTime(...props), + }; +}); + +const mockUseSourcererDataView = jest.fn().mockReturnValue({ selectedPatterns }); +jest.mock('../../../common/containers/sourcerer', () => { + return { + useSourcererDataView: (...props: unknown[]) => mockUseSourcererDataView(...props), + }; +}); + +const mockUseUserDetails = useUserDetails as jest.Mock; +jest.mock('../../../explore/users/containers/users/details'); + +const mockUseRiskScore = useRiskScore as jest.Mock; +jest.mock('../../../explore/containers/risk_score'); + +describe('', () => { + describe('license is valid', () => { + it('should render ip addresses and user risk classification', () => { + mockUseUserDetails.mockReturnValue([false, { userDetails: userData }]); + mockUseRiskScore.mockReturnValue({ data: riskLevel, isLicenseValid: true }); + + const { getByTestId } = render( + + + + ); + + expect(getByTestId(ENTITIES_USER_OVERVIEW_IP_TEST_ID)).toHaveTextContent(ip); + expect(getByTestId(TECHNICAL_PREVIEW_ICON_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(ENTITIES_USER_OVERVIEW_RISK_LEVEL_TEST_ID)).toHaveTextContent('Medium'); + }); + + it('should render correctly if returned data is null', () => { + mockUseUserDetails.mockReturnValue([false, { userDetails: null }]); + mockUseRiskScore.mockReturnValue({ data: null, isLicenseValid: true }); + + const { getByTestId } = render( + + + + ); + expect(getByTestId(ENTITIES_USER_OVERVIEW_IP_TEST_ID)).toHaveTextContent('—'); + expect(getByTestId(TECHNICAL_PREVIEW_ICON_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(ENTITIES_USER_OVERVIEW_RISK_LEVEL_TEST_ID)).toHaveTextContent('Unknown'); + }); + }); + + describe('license is not valid', () => { + it('should render ip but not user risk classification', () => { + mockUseUserDetails.mockReturnValue([false, { userDetails: userData }]); + mockUseRiskScore.mockReturnValue({ data: riskLevel, isLicenseValid: false }); + const { getByTestId, queryByTestId } = render( + + + + ); + + expect(getByTestId(ENTITIES_USER_OVERVIEW_IP_TEST_ID)).toHaveTextContent(ip); + expect(queryByTestId(ENTITIES_USER_OVERVIEW_RISK_LEVEL_TEST_ID)).not.toBeInTheDocument(); + }); + + it('should render correctly if returned data is null', () => { + mockUseUserDetails.mockReturnValue([false, { userDetails: null }]); + mockUseRiskScore.mockReturnValue({ data: null, isLicenseValid: false }); + const { getByTestId, queryByTestId } = render( + + + + ); + + expect(getByTestId(ENTITIES_USER_OVERVIEW_IP_TEST_ID)).toHaveTextContent('—'); + expect(queryByTestId(TECHNICAL_PREVIEW_ICON_TEST_ID)).not.toBeInTheDocument(); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/user_entity_overview.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/user_entity_overview.tsx new file mode 100644 index 0000000000000..bf793a1d058ac --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/user_entity_overview.tsx @@ -0,0 +1,152 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiBetaBadge } from '@elastic/eui'; +import { getOr } from 'lodash/fp'; +import styled from 'styled-components'; +import type { DescriptionList } from '../../../../common/utility_types'; +import { + buildUserNamesFilter, + RiskScoreEntity, + RiskSeverity, +} from '../../../../common/search_strategy'; +import { DefaultFieldRenderer } from '../../../timelines/components/field_renderers/field_renderers'; +import { NetworkDetailsLink } from '../../../common/components/links'; +import { DescriptionListStyled } from '../../../common/components/page'; +import { OverviewDescriptionList } from '../../../common/components/overview_description_list'; +import { RiskScore } from '../../../explore/components/risk_score/severity/common'; +import { getEmptyTagValue } from '../../../common/components/empty_value'; +import { useSourcererDataView } from '../../../common/containers/sourcerer'; +import { useGlobalTime } from '../../../common/containers/use_global_time'; +import { useRiskScore } from '../../../explore/containers/risk_score'; +import { useUserDetails } from '../../../explore/users/containers/users/details'; +import * as i18n from '../../../overview/components/user_overview/translations'; +import { TECHNICAL_PREVIEW_TITLE, TECHNICAL_PREVIEW_MESSAGE } from './translations'; +import { + TECHNICAL_PREVIEW_ICON_TEST_ID, + ENTITIES_USER_OVERVIEW_TEST_ID, + ENTITIES_USER_OVERVIEW_IP_TEST_ID, + ENTITIES_USER_OVERVIEW_RISK_LEVEL_TEST_ID, +} from './test_ids'; + +const StyledEuiBetaBadge = styled(EuiBetaBadge)` + margin-left: ${({ theme }) => theme.eui.euiSizeXS}; +`; + +const CONTEXT_ID = `flyout-user-entity-overview`; + +export interface UserEntityOverviewProps { + /** + * User name for looking up user related ip addresses and risk classification + */ + userName: string; +} + +/** + * User preview content for the entities preview in right flyout. It contains ip addresses and risk classification + */ +export const UserEntityOverview: React.FC = ({ userName }) => { + const { from, to } = useGlobalTime(); + const { selectedPatterns } = useSourcererDataView(); + + const timerange = useMemo( + () => ({ + from, + to, + }), + [from, to] + ); + + const filterQuery = useMemo( + () => (userName ? buildUserNamesFilter([userName]) : undefined), + [userName] + ); + const [_, { userDetails: data }] = useUserDetails({ + endDate: to, + userName, + indexNames: selectedPatterns, + startDate: from, + }); + + const { data: userRisk, isLicenseValid } = useRiskScore({ + filterQuery, + riskEntity: RiskScoreEntity.user, + timerange, + }); + + const descriptionList: DescriptionList[] = useMemo( + () => [ + { + title: i18n.HOST_IP, + description: ( + (ip != null ? : getEmptyTagValue())} + /> + ), + }, + ], + [data] + ); + + const [userRiskLevel] = useMemo(() => { + const userRiskData = userRisk && userRisk.length > 0 ? userRisk[0] : undefined; + + return [ + { + title: ( + <> + {i18n.USER_RISK_CLASSIFICATION} + + + ), + + description: ( + <> + {userRiskData ? ( + + ) : ( + + )} + + ), + }, + ]; + }, [userRisk]); + + return ( + + + + + + {isLicenseValid && ( + + )} + + + ); +}; + +UserEntityOverview.displayName = 'UserEntityOverview'; diff --git a/x-pack/plugins/security_solution/public/flyout/right/mocks/mock_context.ts b/x-pack/plugins/security_solution/public/flyout/right/mocks/mock_context.ts index 6765589b668da..a4d01bda6bc21 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/mocks/mock_context.ts +++ b/x-pack/plugins/security_solution/public/flyout/right/mocks/mock_context.ts @@ -18,6 +18,10 @@ export const mockGetFieldsData = (field: string): string[] => { return ['low']; case ALERT_RISK_SCORE: return ['0']; + case 'host.name': + return ['host1']; + case 'user.name': + return ['user1']; default: return []; } diff --git a/x-pack/plugins/security_solution/public/flyout/right/tabs/overview_tab.tsx b/x-pack/plugins/security_solution/public/flyout/right/tabs/overview_tab.tsx index 8a955e0bbe9a0..0c9e57f99d22f 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/tabs/overview_tab.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/tabs/overview_tab.tsx @@ -10,6 +10,7 @@ import React, { memo } from 'react'; import { EuiHorizontalRule } from '@elastic/eui'; import { InvestigationSection } from '../components/investigation_section'; import { DescriptionSection } from '../components/description_section'; +import { InsightsSection } from '../components/insights_section'; /** * Overview view displayed in the document details expandable flyout right section @@ -20,6 +21,8 @@ export const OverviewTab: FC = memo(() => { + + ); }); diff --git a/x-pack/plugins/security_solution/public/flyout/shared/utils.test.tsx b/x-pack/plugins/security_solution/public/flyout/shared/utils.test.tsx new file mode 100644 index 0000000000000..ef2a14728bae7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/shared/utils.test.tsx @@ -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 { getField } from './utils'; + +describe('test getField', () => { + it('should return the string value if field is a string', () => { + expect(getField('test string')).toBe('test string'); + }); + it('should return the first string value if field is a string array', () => { + expect(getField(['string1', 'string2', 'string3'])).toBe('string1'); + }); + + it('should return null if field is not string or string array', () => { + expect(getField(100)).toBe(null); + expect(getField([1, 2, 3])).toBe(null); + expect(getField({ test: 'test string' })).toBe(null); + expect(getField(null)).toBe(null); + }); + + it('should return fallback value if field is not string or string array and emptyValue is provided', () => { + expect(getField(100, '-')).toBe('-'); + expect(getField([1, 2, 3], '-')).toBe('-'); + expect(getField({ test: 'test string' }, '-')).toBe('-'); + expect(getField(null, '-')).toBe('-'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/shared/utils.tsx b/x-pack/plugins/security_solution/public/flyout/shared/utils.tsx new file mode 100644 index 0000000000000..88f0c399d723d --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/shared/utils.tsx @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/** + * Helper function to retrieve a field's value (used in combination with the custom hook useGetFieldsData (https://github.com/elastic/kibana/blob/main/x-pack/plugins/security_solution/public/common/hooks/use_get_fields_data.ts) + * @param field type unknown or unknown[] + * @param emptyValue optional parameter to return if field is incorrect + * @return the field's value, or null/emptyValue + */ +export const getField = (field: unknown | unknown[], emptyValue?: string) => { + if (typeof field === 'string') { + return field; + } else if (Array.isArray(field) && field.length > 0 && typeof field[0] === 'string') { + return field[0]; + } + return emptyValue ?? null; +}; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_filters.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_filters.tsx index 9215fd5fa3cee..e1f5ee2fb9383 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_filters.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_filters.tsx @@ -10,6 +10,8 @@ import type { DurationRange, OnRefreshChangeProps, } from '@elastic/eui/src/components/date_picker/types'; +import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; +import { ActionsLogWithRuleToggle } from './actions_log_with_rule_toggle'; import type { useGetEndpointActionList } from '../../../hooks'; import { type DateRangePickerValues, @@ -50,6 +52,9 @@ export const ActionsLogFilters = memo( 'data-test-subj'?: string; }) => { const getTestId = useTestIdGenerator(dataTestSubj); + const responseActionsEnabled = useIsExperimentalFeatureEnabled( + 'endpointResponseActionsEnabled' + ); const filters = useMemo(() => { return ( <> @@ -73,6 +78,9 @@ export const ActionsLogFilters = memo( onChangeFilterOptions={onChangeStatusesFilter} data-test-subj={dataTestSubj} /> + {responseActionsEnabled && ( + + )} ); }, [ @@ -81,6 +89,7 @@ export const ActionsLogFilters = memo( onChangeCommandsFilter, onChangeHostsFilter, onChangeStatusesFilter, + responseActionsEnabled, showHostsFilter, ]); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_table.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_table.tsx index 51af5a943cc98..f89046154d1d3 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_table.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_table.tsx @@ -6,7 +6,6 @@ */ import React, { memo, useCallback, useEffect, useMemo, useState } from 'react'; -import type { CriteriaWithPagination } from '@elastic/eui'; import { EuiI18nNumber, EuiAvatar, @@ -19,10 +18,14 @@ import { EuiText, EuiToolTip, type HorizontalAlignment, + type CriteriaWithPagination, } from '@elastic/eui'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { FormattedMessage } from '@kbn/i18n-react'; +import { SecurityPageName } from '../../../../../common/constants'; +import { getRuleDetailsUrl } from '../../../../common/components/link_to'; +import { SecuritySolutionLinkAnchor } from '../../../../common/components/links'; import type { ActionListApiResponse } from '../../../../../common/endpoint/types'; import type { EndpointActionListRequestQuery } from '../../../../../common/endpoint/schema/actions'; import { FormattedDate } from '../../../../common/components/formatted_date'; @@ -98,24 +101,46 @@ const getResponseActionListTableColumns = ({ }, }, { - field: 'createdBy', name: TABLE_COLUMN_NAMES.user, width: !showHostNames ? '21%' : '14%', truncateText: true, - render: (userId: ActionListApiResponse['data'][number]['createdBy']) => { + render: ({ createdBy, ruleId }: ActionListApiResponse['data'][number]) => { + if (createdBy === 'unknown' && ruleId) { + return ( + + + + {UX_MESSAGES.triggeredByRule} + + + + ); + } return ( + } > - + - {userId} + {createdBy} diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_with_rule_toggle.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_with_rule_toggle.tsx new file mode 100644 index 0000000000000..84384024b380b --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_with_rule_toggle.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiFilterButton } from '@elastic/eui'; +import React, { useCallback } from 'react'; +import { useActionHistoryUrlParams } from './use_action_history_url_params'; +import { FILTER_NAMES } from '../translations'; + +interface ActionsLogWithRuleToggleProps { + isFlyout: boolean; + dataTestSubj?: string; +} + +export const ActionsLogWithRuleToggle = React.memo( + ({ isFlyout, dataTestSubj }: ActionsLogWithRuleToggleProps) => { + const { withAutomatedActions: withAutomatedActionsUrlParam, setUrlWithAutomatedActions } = + useActionHistoryUrlParams(); + + const onClick = useCallback(() => { + if (!isFlyout) { + // set and show `withAutomatedActions` URL param on history page + setUrlWithAutomatedActions(!withAutomatedActionsUrlParam); + } + }, [isFlyout, setUrlWithAutomatedActions, withAutomatedActionsUrlParam]); + + return ( + + {FILTER_NAMES.automated} + + ); + } +); + +ActionsLogWithRuleToggle.displayName = 'ActionsLogWithRuleToggle'; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/use_action_history_url_params.ts b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/use_action_history_url_params.ts index b483ed8a132cc..3201aad1478d4 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/use_action_history_url_params.ts +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/use_action_history_url_params.ts @@ -23,6 +23,7 @@ interface UrlParamsActionsLogFilters { endDate: string; users: string; withOutputs: string; + withAutomatedActions: boolean; } interface ActionsLogFiltersFromUrlParams { @@ -32,18 +33,27 @@ interface ActionsLogFiltersFromUrlParams { statuses?: ResponseActionStatus[]; startDate?: string; endDate?: string; + withAutomatedActions?: boolean; setUrlActionsFilters: (commands: UrlParamsActionsLogFilters['commands']) => void; setUrlDateRangeFilters: ({ startDate, endDate }: { startDate: string; endDate: string }) => void; setUrlHostsFilters: (agentIds: UrlParamsActionsLogFilters['hosts']) => void; setUrlStatusesFilters: (statuses: UrlParamsActionsLogFilters['statuses']) => void; setUrlUsersFilters: (users: UrlParamsActionsLogFilters['users']) => void; setUrlWithOutputs: (outputs: UrlParamsActionsLogFilters['withOutputs']) => void; + setUrlWithAutomatedActions: (outputs: UrlParamsActionsLogFilters['withAutomatedActions']) => void; users?: string[]; } type FiltersFromUrl = Pick< ActionsLogFiltersFromUrlParams, - 'commands' | 'hosts' | 'withOutputs' | 'statuses' | 'users' | 'startDate' | 'endDate' + | 'commands' + | 'hosts' + | 'withOutputs' + | 'statuses' + | 'users' + | 'startDate' + | 'endDate' + | 'withAutomatedActions' >; export const actionsLogFiltersFromUrlParams = ( @@ -57,6 +67,7 @@ export const actionsLogFiltersFromUrlParams = ( endDate: 'now', users: [], withOutputs: [], + withAutomatedActions: undefined, }; const urlCommands = urlParams.commands @@ -100,6 +111,7 @@ export const actionsLogFiltersFromUrlParams = ( actionsLogFilters.endDate = urlParams.endDate ? String(urlParams.endDate) : undefined; actionsLogFilters.users = urlUsers.length ? urlUsers : undefined; actionsLogFilters.withOutputs = urlWithOutputs.length ? urlWithOutputs : undefined; + actionsLogFilters.withAutomatedActions = urlParams.withAutomatedActions ? true : undefined; return actionsLogFilters; }; @@ -195,6 +207,19 @@ export const useActionHistoryUrlParams = (): ActionsLogFiltersFromUrlParams => { [history, location, toUrlParams, urlParams] ); + const setUrlWithAutomatedActions = useCallback( + (rule: boolean) => { + history.push({ + ...location, + search: toUrlParams({ + ...urlParams, + withAutomatedActions: rule ? 'true' : undefined, + }), + }); + }, + [history, location, toUrlParams, urlParams] + ); + useEffect(() => { setActionsLogFilters((prevState) => { return { @@ -212,5 +237,6 @@ export const useActionHistoryUrlParams = (): ActionsLogFiltersFromUrlParams => { setUrlWithOutputs, setUrlStatusesFilters, setUrlUsersFilters, + setUrlWithAutomatedActions, }; }; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx index 7a2bf278b811d..09f12c91934e2 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx @@ -289,6 +289,7 @@ describe.skip('Response actions history', () => { statuses: [], userIds: [], withOutputs: [], + withAutomatedActions: true, }, expect.anything() ); @@ -963,6 +964,7 @@ describe.skip('Response actions history', () => { statuses: ['failed', 'pending'], userIds: [], withOutputs: [], + withAutomatedActions: true, }, expect.anything() ); @@ -1164,6 +1166,7 @@ describe.skip('Response actions history', () => { statuses: [], userIds: [], withOutputs: [], + withAutomatedActions: true, }, expect.anything() ); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.tsx index e94cc67c81bdc..eee4638ede8f2 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.tsx @@ -53,6 +53,7 @@ export const ResponseActionsLog = memo< startDate: startDateFromUrl, endDate: endDateFromUrl, users: usersFromUrl, + withAutomatedActions: withAutomatedActionsFromUrl, withOutputs: withOutputsFromUrl, setUrlWithOutputs, } = useActionHistoryUrlParams(); @@ -70,6 +71,7 @@ export const ResponseActionsLog = memo< statuses: [], userIds: [], withOutputs: [], + withAutomatedActions: true, }); // update query state from URL params @@ -86,6 +88,7 @@ export const ResponseActionsLog = memo< : prevState.statuses, userIds: usersFromUrl?.length ? usersFromUrl : prevState.userIds, withOutputs: withOutputsFromUrl?.length ? withOutputsFromUrl : prevState.withOutputs, + withAutomatedActions: !!withAutomatedActionsFromUrl, })); } }, [ @@ -96,6 +99,7 @@ export const ResponseActionsLog = memo< setQueryParams, usersFromUrl, withOutputsFromUrl, + withAutomatedActionsFromUrl, ]); // date range picker state and handlers diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx index fb5a00a3dba63..901d0c68b9040 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/translations.tsx @@ -93,6 +93,9 @@ export const TABLE_COLUMN_NAMES = Object.freeze({ status: i18n.translate('xpack.securitySolution.responseActionsList.list.status', { defaultMessage: 'Status', }), + rule: i18n.translate('xpack.securitySolution.responseActionsList.list.rule', { + defaultMessage: 'Rule', + }), }); export const UX_MESSAGES = Object.freeze({ @@ -164,6 +167,12 @@ export const UX_MESSAGES = Object.freeze({ records: totalItemCount, }, }), + triggeredByRule: i18n.translate( + 'xpack.securitySolution.responseActionsList.list.rule.triggeredByRule', + { + defaultMessage: 'Triggered by rule', + } + ), }); export const FILTER_NAMES = Object.freeze({ @@ -179,6 +188,9 @@ export const FILTER_NAMES = Object.freeze({ users: i18n.translate('xpack.securitySolution.responseActionsList.list.filter.users', { defaultMessage: 'Filter by username', }), + automated: i18n.translate('xpack.securitySolution.responseActionsList.list.filter.automated', { + defaultMessage: 'Automated', + }), }); export const ARIA_LABELS = Object.freeze({ diff --git a/x-pack/plugins/security_solution/public/management/cypress/tasks/run_endpoint_loader.ts b/x-pack/plugins/security_solution/public/management/cypress/tasks/run_endpoint_loader.ts index 22bd1e7dee6f5..c235128675f71 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/tasks/run_endpoint_loader.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/tasks/run_endpoint_loader.ts @@ -26,5 +26,5 @@ export const runEndpointLoaderScript = () => { // FIXME: remove use of cli script and use instead data loaders const script = `node scripts/endpoint/resolver_generator.js --node="${ES_URL.toString()}" --kibana="${KBN_URL.toString()}" --delete --numHosts=1 --numDocs=1 --fleet --withNewUser=santaEndpoint:changeme --anc=1 --gen=1 --ch=1 --related=1 --relAlerts=1`; - cy.exec(script, { env: { NODE_TLS_REJECT_UNAUTHORIZED: 1 } }); + cy.exec(script, { env: { NODE_TLS_REJECT_UNAUTHORIZED: 1 }, timeout: 180000 }); }; diff --git a/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_get_endpoint_action_list.test.ts b/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_get_endpoint_action_list.test.ts index f588f96949e5d..0296d83469ccf 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_get_endpoint_action_list.test.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_get_endpoint_action_list.test.ts @@ -51,6 +51,7 @@ describe('useGetEndpointActionList hook', () => { pageSize: 20, startDate: 'now-5d', endDate: 'now', + withAutomatedActions: true, }) ); @@ -65,6 +66,7 @@ describe('useGetEndpointActionList hook', () => { pageSize: 20, startDate: 'now-5d', userIds: ['*elastic*', '*citsale*'], + withAutomatedActions: true, }, }); }); @@ -73,7 +75,7 @@ describe('useGetEndpointActionList hook', () => { await renderReactQueryHook( () => useGetEndpointActionList( - {}, + { withAutomatedActions: true }, { queryKey: ['1', '2'], enabled: false, diff --git a/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_get_endpoint_action_list.ts b/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_get_endpoint_action_list.ts index 546b1b5f42669..e4cd03987a5d0 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_get_endpoint_action_list.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_get_endpoint_action_list.ts @@ -47,6 +47,8 @@ export const useGetEndpointActionList = ( statuses: query.statuses, userIds, withOutputs: query.withOutputs, + withAutomatedActions: query.withAutomatedActions, + alertId: query.alertId, }, }); }, diff --git a/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx b/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx index f61ee1bf1fb1c..34692b8cc12d3 100644 --- a/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx @@ -18,9 +18,10 @@ import { convertToBuildEsQuery } from '../../../common/lib/kuery'; import type { GlobalTimeArgs } from '../../../common/containers/use_global_time'; import { useInvalidFilterQuery } from '../../../common/hooks/use_invalid_filter_query'; import { - hostNameExistsFilter, + fieldNameExistsFilter, sourceOrDestinationIpExistsFilter, } from '../../../common/components/visualization_actions/utils'; +import { SecurityPageName } from '../../../../common/constants'; interface Props extends Pick { filters: Filter[]; @@ -46,7 +47,7 @@ const EventCountsComponent: React.FC = ({ config: getEsQueryConfig(uiSettings), indexPattern, queries: [query], - filters: [...filters, ...hostNameExistsFilter], + filters: [...filters, ...fieldNameExistsFilter(SecurityPageName.hosts)], }), [filters, indexPattern, query, uiSettings] ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.test.tsx index 555b1a2eb84fa..eee8d15ff19ac 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.test.tsx @@ -144,6 +144,7 @@ describe('event details panel component', () => { }, osquery: { OsqueryResults: jest.fn().mockReturnValue(null), + fetchAllLiveQueries: jest.fn().mockReturnValue({ data: { data: { items: [] } } }), }, }, }); diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index 2c7aa6340a5cf..f1450738e8d36 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -92,7 +92,7 @@ export interface StartPlugins { ml?: MlPluginStart; spaces?: SpacesPluginStart; dataViewFieldEditor: IndexPatternFieldEditorStart; - osquery?: OsqueryPluginStart; + osquery: OsqueryPluginStart; security: SecurityPluginStart; cloudDefend: CloudDefendPluginStart; cloudSecurityPosture: CspClientPluginStart; 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 c451198a27208..8b91273a7cae7 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 @@ -45,6 +45,7 @@ export class ActionResponderService extends BaseRunningService { const { data: actions } = await fetchEndpointActionList(kbnClient, { page: nextPage++, pageSize: 100, + withAutomatedActions: true, }); if (actions.length === 0) { 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 5547f74bc0989..995aa73835fca 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 @@ -46,7 +46,7 @@ export const sleep = (ms: number = 1000) => new Promise((r) => setTimeout(r, ms) export const fetchEndpointActionList = async ( kbn: KbnClient, - options: EndpointActionListRequestQuery = {} + options: EndpointActionListRequestQuery = { withAutomatedActions: true } ): Promise => { try { return ( diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/list_handler.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/list_handler.test.ts index b9b67b2197b1d..2854f0cd6ef8d 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/list_handler.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/list_handler.test.ts @@ -96,21 +96,22 @@ describe('Action List Handler', () => { }); describe('Internals', () => { + const defaultParams = { pageSize: 10, page: 1, withAutomatedActions: true }; it('should return `notFound` when actions index does not exist', async () => { mockDoesLogsEndpointActionsIndexExist.mockResolvedValue(false); - await actionListHandler({ pageSize: 10, page: 1 }); + await actionListHandler(defaultParams); expect(mockResponse.notFound).toHaveBeenCalledWith({ body: 'index_not_found_exception', }); }); it('should return `ok` when actions index exists', async () => { - await actionListHandler({ pageSize: 10, page: 1 }); + await actionListHandler(defaultParams); expect(mockResponse.ok).toHaveBeenCalled(); }); it('should call `getActionListByStatus` when statuses filter values are provided', async () => { - await actionListHandler({ pageSize: 10, page: 1, statuses: ['failed', 'pending'] }); + await actionListHandler({ ...defaultParams, statuses: ['failed', 'pending'] }); expect(mockGetActionListByStatus).toBeCalledWith( expect.objectContaining({ statuses: ['failed', 'pending'] }) ); @@ -123,6 +124,7 @@ describe('Action List Handler', () => { commands: 'running-processes', statuses: 'failed', userIds: 'userX', + withAutomatedActions: true, }); expect(mockGetActionListByStatus).toBeCalledWith( expect.objectContaining({ @@ -137,8 +139,7 @@ describe('Action List Handler', () => { it('should call `getActionList` when statuses filter values are not provided', async () => { await actionListHandler({ - pageSize: 10, - page: 1, + ...defaultParams, commands: ['isolate', 'kill-process'], userIds: ['userX', 'userY'], }); @@ -152,8 +153,7 @@ describe('Action List Handler', () => { it('should correctly format the request when calling `getActionList`', async () => { await actionListHandler({ - page: 1, - pageSize: 10, + ...defaultParams, agentIds: 'agentX', commands: 'isolate', userIds: 'userX', diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/list_handler.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/list_handler.ts index 06496266cf6df..2b9c5b58735b4 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/list_handler.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/list_handler.ts @@ -57,6 +57,8 @@ export const actionListHandler = ( commands, statuses, withOutputs, + withAutomatedActions, + alertId, }, } = req; const esClient = (await context.core).elasticsearch.client.asInternalUser; @@ -74,6 +76,8 @@ export const actionListHandler = ( const requestParams = { withOutputs: formatStringIds(withOutputs), + alertId: formatStringIds(alertId), + withAutomatedActions, commands: formatCommandValues(commands), esClient, elasticAgentIds: formatStringIds(elasticAgentIds), @@ -85,7 +89,6 @@ export const actionListHandler = ( userIds: formatStringIds(userIds), logger, }; - // wrapper method to branch logic for // normal paged search via page, size // vs full search for status filters diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/action_list.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/action_list.ts index f1e6b831cbbce..3ca6f56703e2e 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/action_list.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/action_list.ts @@ -37,6 +37,9 @@ interface OptionalFilterParams { unExpiredOnly?: boolean; /** list of action Ids that should have outputs */ withOutputs?: string[]; + /** Include automated response actions */ + withAutomatedActions?: boolean; + alertId?: string[]; } /** @@ -57,6 +60,7 @@ export const getActionListByStatus = async ({ statuses, userIds, unExpiredOnly = false, + withAutomatedActions, withOutputs, }: OptionalFilterParams & { statuses: ResponseActionStatus[]; @@ -79,6 +83,7 @@ export const getActionListByStatus = async ({ startDate, userIds, unExpiredOnly, + withAutomatedActions, withOutputs, }); @@ -118,6 +123,8 @@ export const getActionList = async ({ userIds, unExpiredOnly = false, withOutputs, + withAutomatedActions, + alertId, }: OptionalFilterParams & { esClient: ElasticsearchClient; logger: Logger; @@ -141,6 +148,8 @@ export const getActionList = async ({ userIds, unExpiredOnly, withOutputs, + withAutomatedActions, + alertId, }); return { @@ -176,6 +185,8 @@ const getActionDetailsList = async ({ userIds, unExpiredOnly, withOutputs, + withAutomatedActions, + alertId, }: GetActionDetailsListParam & { metadataService: EndpointMetadataService }): Promise<{ actionDetails: ActionListApiResponse['data']; totalRecords: number; @@ -197,6 +208,8 @@ const getActionDetailsList = async ({ size, userIds, unExpiredOnly, + withAutomatedActions, + alertId, }); actionRequests = _actionRequests; actionReqIds = actionIds; @@ -297,6 +310,9 @@ const getActionDetailsList = async ({ createdBy: action.createdBy, comment: action.comment, parameters: action.parameters, + alertIds: action.alertIds, + ruleId: action.ruleId, + ruleName: action.ruleName, }; return actionRecord; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/create/index.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/create/index.ts index 36507559ecbb3..76ffbbc08a3e0 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/create/index.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/create/index.ts @@ -59,6 +59,8 @@ export class ActionCreateService { payload: TypeOf & { command: ResponseActionsApiCommandNames; user?: ReturnType; + rule_id?: string; + rule_name?: string; }, casesClient?: CasesClient ): Promise { @@ -110,12 +112,16 @@ export class ActionCreateService { data: { command: payload.command, comment: payload.comment ?? undefined, + ...(payload.alert_ids ? { alert_id: payload.alert_ids } : {}), parameters: getActionParameters() ?? undefined, }, } as Omit, user: { id: payload.user ? payload.user.username : 'unknown', }, + ...(payload.rule_id && payload.rule_name + ? { rule: { id: payload.rule_id, name: payload.rule_name } } + : {}), }; // if .logs-endpoint.actions data stream exists diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/utils.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/utils.ts index 17f8be8c8811f..312287e081559 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/utils.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/utils.ts @@ -58,6 +58,9 @@ interface NormalizedActionRequest { command: ResponseActionsApiCommandNames; comment?: string; parameters?: EndpointActionDataParameterTypes; + alertIds?: string[]; + ruleId?: string; + ruleName?: string; } /** @@ -83,6 +86,9 @@ export const mapToNormalizedActionRequest = ( id: actionRequest.EndpointActions.action_id, type, parameters: actionRequest.EndpointActions.data.parameters, + alertIds: actionRequest.EndpointActions.data.alert_id, + ruleId: actionRequest.rule?.id, + ruleName: actionRequest.rule?.name, }; } diff --git a/x-pack/plugins/security_solution/server/endpoint/utils/action_list_helpers.ts b/x-pack/plugins/security_solution/server/endpoint/utils/action_list_helpers.ts index ee0a2d6e6e2ca..ea0f648a44dc5 100644 --- a/x-pack/plugins/security_solution/server/endpoint/utils/action_list_helpers.ts +++ b/x-pack/plugins/security_solution/server/endpoint/utils/action_list_helpers.ts @@ -36,6 +36,8 @@ export const getActions = async ({ startDate, userIds, unExpiredOnly, + withAutomatedActions, + alertId, }: Omit): Promise<{ actionIds: string[]; actionRequests: TransportResult, unknown>; @@ -50,6 +52,10 @@ export const getActions = async ({ }); } + if (alertId?.length) { + additionalFilters.push({ terms: { 'data.alert_id': alertId } }); + } + if (elasticAgentIds?.length) { additionalFilters.push({ terms: { agents: elasticAgentIds } }); } @@ -75,6 +81,17 @@ export const getActions = async ({ }, ]; + const mustNot: SearchRequest = + withAutomatedActions === false + ? { + must_not: { + exists: { + field: 'data.alert_id', + }, + }, + } + : {}; + if (userIds?.length) { const userIdsKql = userIds.map((userId) => `user_id:${userId}`).join(' or '); const mustClause = toElasticsearchQuery(fromKueryExpression(userIdsKql)); @@ -87,7 +104,10 @@ export const getActions = async ({ from, body: { query: { - bool: { must }, + bool: { + must, + ...mustNot, + }, }, sort: [ { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts index ae76c0d128a98..bfda85063f2b9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts @@ -106,7 +106,6 @@ const createSecuritySolutionRequestContextMock = ( ): jest.Mocked => { const core = clients.core; const kibanaRequest = requestMock.create(); - const licensing = licensingMock.createSetup(); return { core, @@ -136,10 +135,6 @@ const createSecuritySolutionRequestContextMock = ( // TODO: Mock EndpointInternalFleetServicesInterface and return the mocked object. throw new Error('Not implemented'); }), - getQueryRuleAdditionalOptions: { - licensing, - osqueryCreateAction: jest.fn(), - }, }; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/route.ts index 86477bfda3d08..d90fd562a9cc5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_preview/api/preview_rules/route.ts @@ -97,8 +97,6 @@ export const previewRulesRoute = async ( const searchSourceClient = await data.search.searchSource.asScoped(request); const savedObjectsClient = coreContext.savedObjects.client; const siemClient = (await context.securitySolution).getAppClient(); - const { getQueryRuleAdditionalOptions: queryRuleAdditionalOptions } = - await context.securitySolution; const timeframeEnd = request.body.timeframeEnd; let invocationCount = request.body.invocationCount; @@ -304,7 +302,6 @@ export const previewRulesRoute = async ( const queryAlertType = previewRuleTypeWrapper( createQueryAlertType({ ...ruleOptions, - ...queryRuleAdditionalOptions, id: QUERY_RULE_TYPE_ID, name: 'Custom Query Rule', }) @@ -329,7 +326,6 @@ export const previewRulesRoute = async ( const savedQueryAlertType = previewRuleTypeWrapper( createQueryAlertType({ ...ruleOptions, - ...queryRuleAdditionalOptions, id: SAVED_QUERY_RULE_TYPE_ID, name: 'Saved Query Rule', }) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/endpoint_response_action.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/endpoint_response_action.ts new file mode 100644 index 0000000000000..133efd45dfa17 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/endpoint_response_action.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 { each } from 'lodash'; +import type { EndpointAppContextService } from '../../../endpoint/endpoint_app_context_services'; +import type { RuleResponseEndpointAction } from '../../../../common/detection_engine/rule_response_actions/schemas'; +import type { AlertsWithAgentType } from './types'; + +export const endpointResponseAction = ( + responseAction: RuleResponseEndpointAction, + endpointAppContextService: EndpointAppContextService, + { alertIds, agentIds, ruleId, ruleName }: AlertsWithAgentType +) => + each(agentIds, (agent) => + endpointAppContextService.getActionCreateService().createAction({ + endpoint_ids: [agent], + alert_ids: alertIds, + comment: responseAction.params.comment, + command: responseAction.params.command, + rule_id: ruleId, + rule_name: ruleName, + }) + ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/osquery_response_action.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/osquery_response_action.ts new file mode 100644 index 0000000000000..3312c0b42b10f --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/osquery_response_action.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 { each, some } from 'lodash'; +import { containsDynamicQuery } from '@kbn/osquery-plugin/common/utils/replace_params_query'; +import type { SetupPlugins } from '../../../plugin_contract'; +import type { RuleResponseOsqueryAction } from '../../../../common/detection_engine/rule_response_actions/schemas'; +import type { AlertsWithAgentType } from './types'; + +export const osqueryResponseAction = ( + responseAction: RuleResponseOsqueryAction, + osqueryCreateAction: SetupPlugins['osquery']['osqueryCreateAction'], + { alerts, alertIds, agentIds }: AlertsWithAgentType +) => { + const temporaryQueries = responseAction.params.queries?.length + ? responseAction.params.queries + : [{ query: responseAction.params.query }]; + const containsDynamicQueries = some( + temporaryQueries, + (query) => query.query && containsDynamicQuery(query.query) + ); + + const { savedQueryId, packId, queries, ecsMapping, ...rest } = responseAction.params; + + if (!containsDynamicQueries) { + return osqueryCreateAction({ + ...rest, + queries, + ecs_mapping: ecsMapping, + saved_query_id: savedQueryId, + agent_ids: agentIds, + alert_ids: alertIds, + }); + } + each(alerts, (alert) => { + return osqueryCreateAction( + { + ...rest, + queries, + ecs_mapping: ecsMapping, + saved_query_id: savedQueryId, + agent_ids: alert.agent?.id ? [alert.agent.id] : [], + alert_ids: [(alert as unknown as { _id: string })._id], + }, + alert + ); + }); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/schedule_notification_response_actions.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/schedule_notification_response_actions.test.ts index ce0dad15edb1d..d41e3a374ba75 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/schedule_notification_response_actions.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/schedule_notification_response_actions.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { scheduleNotificationResponseActions } from './schedule_notification_response_actions'; +import { getScheduleNotificationResponseActionsService } from './schedule_notification_response_actions'; import type { RuleResponseAction } from '../../../../common/detection_engine/rule_response_actions/schemas'; import { RESPONSE_ACTION_TYPES } from '../../../../common/detection_engine/rule_response_actions/schemas'; @@ -53,11 +53,16 @@ describe('ScheduleNotificationResponseActions', () => { saved_query_id: undefined, ecs_mapping: { testField: { field: 'testField', value: 'testValue' } }, }; + const osqueryActionMock = jest.fn(); + const endpointActionMock = jest.fn(); + + const scheduleNotificationResponseActions = getScheduleNotificationResponseActionsService({ + osqueryCreateAction: osqueryActionMock, + endpointAppContextService: endpointActionMock as never, + }); const simpleQuery = 'select * from uptime'; it('should handle osquery response actions with query', async () => { - const osqueryActionMock = jest.fn(); - const responseActions: RuleResponseAction[] = [ { actionTypeId: RESPONSE_ACTION_TYPES.OSQUERY, @@ -67,7 +72,7 @@ describe('ScheduleNotificationResponseActions', () => { }, }, ]; - scheduleNotificationResponseActions({ signals, responseActions }, osqueryActionMock); + scheduleNotificationResponseActions({ signals, responseActions }); expect(osqueryActionMock).toHaveBeenCalledWith({ ...defaultQueryResultParams, @@ -76,8 +81,6 @@ describe('ScheduleNotificationResponseActions', () => { // }); it('should handle osquery response actions with packs', async () => { - const osqueryActionMock = jest.fn(); - const responseActions: RuleResponseAction[] = [ { actionTypeId: RESPONSE_ACTION_TYPES.OSQUERY, @@ -94,7 +97,7 @@ describe('ScheduleNotificationResponseActions', () => { }, }, ]; - scheduleNotificationResponseActions({ signals, responseActions }, osqueryActionMock); + scheduleNotificationResponseActions({ signals, responseActions }); expect(osqueryActionMock).toHaveBeenCalledWith({ ...defaultPackResultParams, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/schedule_notification_response_actions.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/schedule_notification_response_actions.ts index cc04444dec34a..4fbcc0592ee69 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/schedule_notification_response_actions.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/schedule_notification_response_actions.ts @@ -5,83 +5,64 @@ * 2.0. */ -import { uniq, reduce, some, each } from 'lodash'; -import { containsDynamicQuery } from '@kbn/osquery-plugin/common/utils/replace_params_query'; +import { reduce, each, uniq } from 'lodash'; import type { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common'; -import type { RuleResponseAction } from '../../../../common/detection_engine/rule_response_actions/schemas'; -import { RESPONSE_ACTION_TYPES } from '../../../../common/detection_engine/rule_response_actions/schemas'; +import { ALERT_RULE_NAME, ALERT_RULE_UUID } from '@kbn/rule-data-utils'; +import type { EndpointAppContextService } from '../../../endpoint/endpoint_app_context_services'; import type { SetupPlugins } from '../../../plugin_contract'; +import { RESPONSE_ACTION_TYPES } from '../../../../common/detection_engine/rule_response_actions/schemas'; +import { osqueryResponseAction } from './osquery_response_action'; +import { endpointResponseAction } from './endpoint_response_action'; +import type { AlertsWithAgentType } from './types'; +import type { ScheduleNotificationActions } from '../rule_types/types'; type Alerts = Array; -interface ScheduleNotificationActions { - signals: unknown[]; - responseActions: RuleResponseAction[]; +interface ScheduleNotificationResponseActionsService { + endpointAppContextService: EndpointAppContextService; + osqueryCreateAction: SetupPlugins['osquery']['osqueryCreateAction']; } -interface AlertsWithAgentType { - alerts: Alerts; - agents: string[]; - alertIds: string[]; -} +export const getScheduleNotificationResponseActionsService = + ({ + osqueryCreateAction, + endpointAppContextService, + }: ScheduleNotificationResponseActionsService) => + ({ signals, responseActions, hasEnterpriseLicense }: ScheduleNotificationActions) => { + const filteredAlerts = (signals as Alerts).filter((alert) => alert.agent?.id); -export const scheduleNotificationResponseActions = ( - { signals, responseActions }: ScheduleNotificationActions, - osqueryCreateAction?: SetupPlugins['osquery']['osqueryCreateAction'] -) => { - const filteredAlerts = (signals as Alerts).filter((alert) => alert.agent?.id); + const { alerts, agentIds, alertIds }: AlertsWithAgentType = reduce( + filteredAlerts, + (acc, alert) => { + const agentId = alert.agent?.id; + if (agentId !== undefined) { + return { + alerts: [...acc.alerts, alert], + agentIds: uniq([...acc.agentIds, agentId]), + alertIds: [...acc.alertIds, (alert as unknown as { _id: string })._id], + }; + } + return acc; + }, + { alerts: [], agentIds: [], alertIds: [] } as AlertsWithAgentType + ); - const { alerts, agents, alertIds }: AlertsWithAgentType = reduce( - filteredAlerts, - (acc, alert) => { - const agentId = alert.agent?.id; - if (agentId !== undefined) { - return { - alerts: [...acc.alerts, alert], - agents: [...acc.agents, agentId], - alertIds: [...acc.alertIds, (alert as unknown as { _id: string })._id], - }; + each(responseActions, (responseAction) => { + if (responseAction.actionTypeId === RESPONSE_ACTION_TYPES.OSQUERY && osqueryCreateAction) { + osqueryResponseAction(responseAction, osqueryCreateAction, { + alerts, + alertIds, + agentIds, + }); } - return acc; - }, - { alerts: [], agents: [], alertIds: [] } as AlertsWithAgentType - ); - const agentIds = uniq(agents); - - each(responseActions, (responseAction) => { - if (responseAction.actionTypeId === RESPONSE_ACTION_TYPES.OSQUERY && osqueryCreateAction) { - const temporaryQueries = responseAction.params.queries?.length - ? responseAction.params.queries - : [{ query: responseAction.params.query }]; - const containsDynamicQueries = some( - temporaryQueries, - (query) => query.query && containsDynamicQuery(query.query) - ); - const { savedQueryId, packId, queries, ecsMapping, ...rest } = responseAction.params; - - if (!containsDynamicQueries) { - return osqueryCreateAction({ - ...rest, - queries, - ecs_mapping: ecsMapping, - saved_query_id: savedQueryId, - agent_ids: agentIds, - alert_ids: alertIds, + if (responseAction.actionTypeId === RESPONSE_ACTION_TYPES.ENDPOINT && hasEnterpriseLicense) { + endpointResponseAction(responseAction, endpointAppContextService, { + alerts, + alertIds, + agentIds, + ruleId: alerts[0][ALERT_RULE_UUID], + ruleName: alerts[0][ALERT_RULE_NAME], }); } - each(alerts, (alert) => { - return osqueryCreateAction( - { - ...rest, - queries, - ecs_mapping: ecsMapping, - saved_query_id: savedQueryId, - agent_ids: alert.agent?.id ? [alert.agent.id] : [], - alert_ids: [(alert as unknown as { _id: string })._id], - }, - alert - ); - }); - } - }); -}; + }); + }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/types.ts new file mode 100644 index 0000000000000..d63d390582827 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/types.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common'; + +export type Alerts = Array< + ParsedTechnicalFields & { agent?: { id: string }; process?: { pid: string } } +>; + +export interface AlertsWithAgentType { + alerts: Alerts; + agentIds: string[]; + alertIds: string[]; + ruleId?: string; + ruleName?: string; +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts index 4d6b3120488fa..d3f8e8b42b44e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts @@ -56,7 +56,7 @@ describe('Custom Query Alerts', () => { createQueryAlertType({ eventsTelemetry, licensing, - osqueryCreateAction: () => null, + scheduleNotificationResponseActionsService: () => null, experimentalFeatures: allowedExperimentalValues, logger, version: '1.0.0', @@ -104,7 +104,7 @@ describe('Custom Query Alerts', () => { createQueryAlertType({ eventsTelemetry, licensing, - osqueryCreateAction: () => null, + scheduleNotificationResponseActionsService: () => null, experimentalFeatures: allowedExperimentalValues, logger, version: '1.0.0', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts index 2aa3a708c672a..1fde4b864ac9e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts @@ -27,7 +27,7 @@ export const createQueryAlertType = ( eventsTelemetry, experimentalFeatures, version, - osqueryCreateAction, + scheduleNotificationResponseActionsService, licensing, id, name, @@ -83,8 +83,8 @@ export const createQueryAlertType = ( version, spaceId, bucketHistory: state.suppressionGroupHistory, - osqueryCreateAction, licensing, + scheduleNotificationResponseActionsService, }); }, }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/query.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/query.ts index 32099bf836991..764103151bf24 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/query.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/query.ts @@ -22,9 +22,7 @@ import type { UnifiedQueryRuleParams } from '../../rule_schema'; import type { ExperimentalFeatures } from '../../../../../common/experimental_features'; import { buildReasonMessageForQueryAlert } from '../utils/reason_formatters'; import { withSecuritySpan } from '../../../../utils/with_security_span'; -import { scheduleNotificationResponseActions } from '../../rule_response_actions/schedule_notification_response_actions'; -import type { SetupPlugins } from '../../../../plugin_contract'; -import type { RunOpts } from '../types'; +import type { CreateQueryRuleAdditionalOptions, RunOpts } from '../types'; export const queryExecutor = async ({ runOpts, @@ -34,7 +32,7 @@ export const queryExecutor = async ({ version, spaceId, bucketHistory, - osqueryCreateAction, + scheduleNotificationResponseActionsService, licensing, }: { runOpts: RunOpts; @@ -44,7 +42,7 @@ export const queryExecutor = async ({ version: string; spaceId: string; bucketHistory?: BucketHistory[]; - osqueryCreateAction: SetupPlugins['osquery']['osqueryCreateAction']; + scheduleNotificationResponseActionsService?: CreateQueryRuleAdditionalOptions['scheduleNotificationResponseActionsService']; licensing: LicensingPluginSetup; }) => { const completeRule = runOpts.completeRule; @@ -64,6 +62,7 @@ export const queryExecutor = async ({ const license = await firstValueFrom(licensing.license$); const hasPlatinumLicense = license.hasAtLeast('platinum'); + const hasEnterpriseLicense = license.hasAtLeast('enterprise'); const result = ruleParams.alertSuppression?.groupBy != null && hasPlatinumLicense @@ -97,16 +96,17 @@ export const queryExecutor = async ({ state: {}, }; - if (hasPlatinumLicense) { - if (completeRule.ruleParams.responseActions?.length && result.createdSignalsCount) { - scheduleNotificationResponseActions( - { - signals: result.createdSignals, - responseActions: completeRule.ruleParams.responseActions, - }, - osqueryCreateAction - ); - } + if ( + hasPlatinumLicense && + completeRule.ruleParams.responseActions?.length && + result.createdSignalsCount && + scheduleNotificationResponseActionsService + ) { + scheduleNotificationResponseActionsService({ + signals: result.createdSignals, + responseActions: completeRule.ruleParams.responseActions, + hasEnterpriseLicense, + }); } return result; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts index 36ac78f2f74d5..0dee5eba79cc4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts @@ -31,9 +31,10 @@ import type { } from '@kbn/rule-registry-plugin/server'; import type { EcsFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/ecs_field_map'; import type { TypeOfFieldMap } from '@kbn/rule-registry-plugin/common/field_map'; -import type { LicensingPluginSetup } from '@kbn/licensing-plugin/server'; import type { Filter } from '@kbn/es-query'; +import type { LicensingPluginSetup } from '@kbn/licensing-plugin/server'; +import type { RuleResponseAction } from '../../../../common/detection_engine/rule_response_actions/schemas'; import type { ConfigType } from '../../../config'; import type { SetupPlugins } from '../../../plugin'; import type { CompleteRule, RuleParams } from '../rule_schema'; @@ -150,11 +151,16 @@ export interface CreateRuleOptions { ml?: SetupPlugins['ml']; eventsTelemetry?: ITelemetryEventsSender | undefined; version: string; + licensing: LicensingPluginSetup; } +export interface ScheduleNotificationActions { + signals: unknown[]; + responseActions: RuleResponseAction[]; + hasEnterpriseLicense?: boolean; +} export interface CreateQueryRuleAdditionalOptions { - osqueryCreateAction: SetupPlugins['osquery']['osqueryCreateAction']; - licensing: LicensingPluginSetup; + scheduleNotificationResponseActionsService?: (params: ScheduleNotificationActions) => void; } export interface CreateQueryRuleOptions @@ -182,6 +188,7 @@ export interface RuleRangeTuple { */ export interface SignalSource { [key: string]: SearchTypes; + '@timestamp'?: string; signal?: { /** @@ -294,6 +301,7 @@ export interface SignalHit { '@timestamp': string; event: object; signal: Signal; + [key: string]: SearchTypes; } @@ -340,6 +348,7 @@ export type RuleServices = RuleExecutorServices< AlertInstanceContext, 'default' >; + export interface SearchAfterAndBulkCreateParams { tuple: { to: moment.Moment; diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 3c19a6b6e4c92..2b6048c122f27 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -28,6 +28,7 @@ import { Dataset } from '@kbn/rule-registry-plugin/server'; import type { ListPluginSetup } from '@kbn/lists-plugin/server'; import type { ILicense } from '@kbn/licensing-plugin/server'; +import { getScheduleNotificationResponseActionsService } from './lib/detection_engine/rule_response_actions/schedule_notification_response_actions'; import { siemGuideId, siemGuideConfig } from '../common/guided_onboarding/siem_guide_config'; import { createEqlAlertType, @@ -166,11 +167,6 @@ export class Plugin implements ISecuritySolutionPlugin { const ruleExecutionLogService = createRuleExecutionLogService(config, logger, core, plugins); ruleExecutionLogService.registerEventLogProvider(); - const queryRuleAdditionalOptions: CreateQueryRuleAdditionalOptions = { - licensing: plugins.licensing, - osqueryCreateAction: plugins.osquery.osqueryCreateAction, - }; - const requestContextFactory = new RequestContextFactory({ config, logger, @@ -214,6 +210,7 @@ export class Plugin implements ISecuritySolutionPlugin { ml: plugins.ml, eventsTelemetry: this.telemetryEventsSender, version: pluginContext.env.packageInfo.version, + licensing: plugins.licensing, }; const ruleDataServiceOptions = { @@ -249,6 +246,13 @@ export class Plugin implements ISecuritySolutionPlugin { version: pluginContext.env.packageInfo.version, }; + const queryRuleAdditionalOptions: CreateQueryRuleAdditionalOptions = { + scheduleNotificationResponseActionsService: getScheduleNotificationResponseActionsService({ + endpointAppContextService: this.endpointAppContextService, + osqueryCreateAction: plugins.osquery.osqueryCreateAction, + }), + }; + const securityRuleTypeWrapper = createSecurityRuleTypeWrapper(securityRuleTypeOptions); plugins.alerting.registerType(securityRuleTypeWrapper(createEqlAlertType(ruleOptions))); diff --git a/x-pack/plugins/security_solution/server/request_context_factory.ts b/x-pack/plugins/security_solution/server/request_context_factory.ts index bead5d6088fd7..3bb125cf06914 100644 --- a/x-pack/plugins/security_solution/server/request_context_factory.ts +++ b/x-pack/plugins/security_solution/server/request_context_factory.ts @@ -57,7 +57,7 @@ export class RequestContextFactory implements IRequestContextFactory { ): Promise { const { options, appClientFactory } = this; const { config, core, plugins, endpointAppContextService, ruleExecutionLogService } = options; - const { lists, ruleRegistry, security, licensing, osquery } = plugins; + const { lists, ruleRegistry, security } = plugins; const [, startPlugins] = await core.getStartServices(); const frameworkRequest = await buildFrameworkRequest(context, security, request); @@ -115,11 +115,6 @@ export class RequestContextFactory implements IRequestContextFactory { }, getInternalFleetServices: memoize(() => endpointAppContextService.getInternalFleetServices()), - - getQueryRuleAdditionalOptions: { - licensing, - osqueryCreateAction: osquery.osqueryCreateAction, - }, }; } } diff --git a/x-pack/plugins/security_solution/server/types.ts b/x-pack/plugins/security_solution/server/types.ts index 20688329eb1b2..993d031dec440 100644 --- a/x-pack/plugins/security_solution/server/types.ts +++ b/x-pack/plugins/security_solution/server/types.ts @@ -19,7 +19,6 @@ import type { ListsApiRequestHandlerContext, ExceptionListClient } from '@kbn/li import type { IRuleDataService, AlertsClient } from '@kbn/rule-registry-plugin/server'; import type { Immutable } from '../common/endpoint/types'; -import type { CreateQueryRuleAdditionalOptions } from './lib/detection_engine/rule_types/types'; import { AppClient } from './client'; import type { ConfigType } from './config'; import type { IRuleExecutionLogForRoutes } from './lib/detection_engine/rule_monitoring'; @@ -41,7 +40,6 @@ export interface SecuritySolutionApiRequestHandlerContext { getRacClient: (req: KibanaRequest) => Promise; getExceptionListClient: () => ExceptionListClient | null; getInternalFleetServices: () => EndpointInternalFleetServicesInterface; - getQueryRuleAdditionalOptions: CreateQueryRuleAdditionalOptions; } export type SecuritySolutionRequestHandlerContext = CustomRequestHandlerContext<{ diff --git a/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts b/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts index 83aeed3269f3e..956dfa2af23b5 100644 --- a/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts +++ b/x-pack/plugins/synthetics/common/constants/monitor_defaults.ts @@ -30,6 +30,19 @@ import { ConfigKey } from './monitor_management'; export const DEFAULT_NAMESPACE_STRING = 'default'; +export const ALLOWED_SCHEDULES_IN_MINUTES = [ + '1', + '3', + '5', + '10', + '15', + '20', + '30', + '60', + '120', + '240', +]; + export const DEFAULT_COMMON_FIELDS: CommonFields = { [ConfigKey.MONITOR_TYPE]: DataStream.HTTP, [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.MULTISTEP, @@ -74,7 +87,6 @@ export const DEFAULT_BROWSER_SIMPLE_FIELDS: BrowserSimpleFields = { is_generated_script: false, file_name: '', }, - is_zip_url_tls_enabled: false, }, [ConfigKey.MONITOR_TYPE]: DataStream.BROWSER, [ConfigKey.PARAMS]: '', @@ -85,23 +97,10 @@ export const DEFAULT_BROWSER_SIMPLE_FIELDS: BrowserSimpleFields = { }, [ConfigKey.SOURCE_INLINE]: '', [ConfigKey.SOURCE_PROJECT_CONTENT]: '', - [ConfigKey.SOURCE_ZIP_URL]: '', - [ConfigKey.SOURCE_ZIP_USERNAME]: '', - [ConfigKey.SOURCE_ZIP_PASSWORD]: '', - [ConfigKey.SOURCE_ZIP_FOLDER]: '', - [ConfigKey.SOURCE_ZIP_PROXY_URL]: '', [ConfigKey.TEXT_ASSERTION]: '', [ConfigKey.URLS]: '', [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.MULTISTEP, [ConfigKey.TIMEOUT]: null, - - // Deprecated, slated to be removed in a future version - [ConfigKey.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: undefined, - [ConfigKey.ZIP_URL_TLS_CERTIFICATE]: undefined, - [ConfigKey.ZIP_URL_TLS_KEY]: undefined, - [ConfigKey.ZIP_URL_TLS_KEY_PASSPHRASE]: undefined, - [ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE]: undefined, - [ConfigKey.ZIP_URL_TLS_VERSION]: undefined, }; export const DEFAULT_HTTP_SIMPLE_FIELDS: HTTPSimpleFields = { diff --git a/x-pack/plugins/synthetics/common/constants/monitor_management.ts b/x-pack/plugins/synthetics/common/constants/monitor_management.ts index 78e1829356458..231acff2c6230 100644 --- a/x-pack/plugins/synthetics/common/constants/monitor_management.ts +++ b/x-pack/plugins/synthetics/common/constants/monitor_management.ts @@ -54,11 +54,6 @@ export enum ConfigKey { SCREENSHOTS = 'screenshots', SOURCE_PROJECT_CONTENT = 'source.project.content', SOURCE_INLINE = 'source.inline.script', - SOURCE_ZIP_URL = 'source.zip_url.url', - SOURCE_ZIP_USERNAME = 'source.zip_url.username', - SOURCE_ZIP_PASSWORD = 'source.zip_url.password', - SOURCE_ZIP_FOLDER = 'source.zip_url.folder', - SOURCE_ZIP_PROXY_URL = 'source.zip_url.proxy_url', PROJECT_ID = 'project_id', SYNTHETICS_ARGS = 'synthetics_args', TEXT_ASSERTION = 'playwright_text_assertion', @@ -78,12 +73,6 @@ export enum ConfigKey { URLS = 'urls', USERNAME = 'username', WAIT = 'wait', - ZIP_URL_TLS_CERTIFICATE_AUTHORITIES = 'source.zip_url.ssl.certificate_authorities', - ZIP_URL_TLS_CERTIFICATE = 'source.zip_url.ssl.certificate', - ZIP_URL_TLS_KEY = 'source.zip_url.ssl.key', - ZIP_URL_TLS_KEY_PASSPHRASE = 'source.zip_url.ssl.key_passphrase', - ZIP_URL_TLS_VERIFICATION_MODE = 'source.zip_url.ssl.verification_mode', - ZIP_URL_TLS_VERSION = 'source.zip_url.ssl.supported_protocols', MONITOR_QUERY_ID = 'id', } @@ -99,12 +88,22 @@ export const secretKeys = [ ConfigKey.RESPONSE_RECEIVE_CHECK, ConfigKey.SOURCE_INLINE, ConfigKey.SOURCE_PROJECT_CONTENT, - ConfigKey.SOURCE_ZIP_USERNAME, - ConfigKey.SOURCE_ZIP_PASSWORD, ConfigKey.SYNTHETICS_ARGS, ConfigKey.TLS_KEY, ConfigKey.TLS_KEY_PASSPHRASE, ConfigKey.USERNAME, - ConfigKey.ZIP_URL_TLS_KEY, - ConfigKey.ZIP_URL_TLS_KEY_PASSPHRASE, ] as const; + +export enum LegacyConfigKey { + SOURCE_ZIP_URL = 'source.zip_url.url', + SOURCE_ZIP_USERNAME = 'source.zip_url.username', + SOURCE_ZIP_PASSWORD = 'source.zip_url.password', + SOURCE_ZIP_FOLDER = 'source.zip_url.folder', + SOURCE_ZIP_PROXY_URL = 'source.zip_url.proxy_url', + ZIP_URL_TLS_CERTIFICATE_AUTHORITIES = 'source.zip_url.ssl.certificate_authorities', + ZIP_URL_TLS_CERTIFICATE = 'source.zip_url.ssl.certificate', + ZIP_URL_TLS_KEY = 'source.zip_url.ssl.key', + ZIP_URL_TLS_KEY_PASSPHRASE = 'source.zip_url.ssl.key_passphrase', + ZIP_URL_TLS_VERIFICATION_MODE = 'source.zip_url.ssl.verification_mode', + ZIP_URL_TLS_VERSION = 'source.zip_url.ssl.supported_protocols', +} diff --git a/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts b/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts index 9e9f7283ec38f..1ad7c055aecc3 100644 --- a/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts +++ b/x-pack/plugins/synthetics/common/formatters/browser/formatters.ts @@ -11,9 +11,6 @@ import { arrayToJsonFormatter, objectToJsonFormatter, stringToJsonFormatter, - tlsArrayToYamlFormatter, - tlsValueToStringFormatter, - tlsValueToYamlFormatter, } from '../formatting_utils'; import { tlsFormatters } from '../tls/formatters'; @@ -35,20 +32,6 @@ const throttlingFormatter: Formatter = (fields) => { .join('/'); }; -export const deprecatedZipUrlFormatters = { - [ConfigKey.SOURCE_ZIP_URL]: null, - [ConfigKey.SOURCE_ZIP_USERNAME]: null, - [ConfigKey.SOURCE_ZIP_PASSWORD]: null, - [ConfigKey.SOURCE_ZIP_FOLDER]: null, - [ConfigKey.SOURCE_ZIP_PROXY_URL]: null, - [ConfigKey.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: tlsValueToYamlFormatter, - [ConfigKey.ZIP_URL_TLS_CERTIFICATE]: tlsValueToYamlFormatter, - [ConfigKey.ZIP_URL_TLS_KEY]: tlsValueToYamlFormatter, - [ConfigKey.ZIP_URL_TLS_KEY_PASSPHRASE]: tlsValueToStringFormatter, - [ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE]: tlsValueToStringFormatter, - [ConfigKey.ZIP_URL_TLS_VERSION]: tlsArrayToYamlFormatter, -}; - export const browserFormatters: BrowserFormatMap = { [ConfigKey.SOURCE_PROJECT_CONTENT]: null, [ConfigKey.PARAMS]: null, @@ -68,7 +51,6 @@ export const browserFormatters: BrowserFormatMap = { [ConfigKey.JOURNEY_FILTERS_MATCH]: stringToJsonFormatter, [ConfigKey.JOURNEY_FILTERS_TAGS]: arrayToJsonFormatter, [ConfigKey.THROTTLING_CONFIG]: throttlingFormatter, - ...deprecatedZipUrlFormatters, ...commonFormatters, ...tlsFormatters, }; diff --git a/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.test.ts b/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.test.ts index 9eca4aa70b216..a03c54fc4f34e 100644 --- a/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.test.ts +++ b/x-pack/plugins/synthetics/common/formatters/format_synthetics_policy.test.ts @@ -333,7 +333,7 @@ describe('formatSyntheticsPolicy', () => { __ui: { type: 'yaml', value: - '{"script_source":{"is_generated_script":false,"file_name":""},"is_zip_url_tls_enabled":false,"is_tls_enabled":false}', + '{"script_source":{"is_generated_script":false,"file_name":""},"is_tls_enabled":false}', }, config_id: { type: 'text', @@ -411,44 +411,6 @@ describe('formatSyntheticsPolicy', () => { type: 'text', value: '', }, - 'source.zip_url.folder': { - type: 'text', - value: '', - }, - 'source.zip_url.password': { - type: 'password', - value: '', - }, - 'source.zip_url.proxy_url': { - type: 'text', - value: '', - }, - 'source.zip_url.ssl.certificate': { - type: 'yaml', - }, - 'source.zip_url.ssl.certificate_authorities': { - type: 'yaml', - }, - 'source.zip_url.ssl.key': { - type: 'yaml', - }, - 'source.zip_url.ssl.key_passphrase': { - type: 'text', - }, - 'source.zip_url.ssl.supported_protocols': { - type: 'yaml', - }, - 'source.zip_url.ssl.verification_mode': { - type: 'text', - }, - 'source.zip_url.url': { - type: 'text', - value: '', - }, - 'source.zip_url.username': { - type: 'text', - value: '', - }, synthetics_args: { type: 'text', value: null, @@ -918,39 +880,6 @@ describe('formatSyntheticsPolicy', () => { 'source.project.content': { type: 'text', }, - 'source.zip_url.folder': { - type: 'text', - }, - 'source.zip_url.password': { - type: 'password', - }, - 'source.zip_url.proxy_url': { - type: 'text', - }, - 'source.zip_url.ssl.certificate': { - type: 'yaml', - }, - 'source.zip_url.ssl.certificate_authorities': { - type: 'yaml', - }, - 'source.zip_url.ssl.key': { - type: 'yaml', - }, - 'source.zip_url.ssl.key_passphrase': { - type: 'text', - }, - 'source.zip_url.ssl.supported_protocols': { - type: 'yaml', - }, - 'source.zip_url.ssl.verification_mode': { - type: 'text', - }, - 'source.zip_url.url': { - type: 'text', - }, - 'source.zip_url.username': { - type: 'text', - }, synthetics_args: { type: 'text', }, @@ -1147,10 +1076,6 @@ const testNewPolicy = { 'service.name': { type: 'text' }, timeout: { type: 'text' }, tags: { type: 'yaml' }, - 'source.zip_url.url': { type: 'text' }, - 'source.zip_url.username': { type: 'text' }, - 'source.zip_url.folder': { type: 'text' }, - 'source.zip_url.password': { type: 'password' }, 'source.inline.script': { type: 'yaml' }, 'source.project.content': { type: 'text' }, params: { type: 'yaml' }, @@ -1161,13 +1086,6 @@ const testNewPolicy = { 'throttling.config': { type: 'text' }, 'filter_journeys.tags': { type: 'yaml' }, 'filter_journeys.match': { type: 'text' }, - 'source.zip_url.ssl.certificate_authorities': { type: 'yaml' }, - 'source.zip_url.ssl.certificate': { type: 'yaml' }, - 'source.zip_url.ssl.key': { type: 'yaml' }, - 'source.zip_url.ssl.key_passphrase': { type: 'text' }, - 'source.zip_url.ssl.verification_mode': { type: 'text' }, - 'source.zip_url.ssl.supported_protocols': { type: 'yaml' }, - 'source.zip_url.proxy_url': { type: 'text' }, location_name: { value: 'Fleet managed', type: 'text' }, id: { type: 'text' }, config_id: { type: 'text' }, @@ -1212,7 +1130,6 @@ const browserConfig: any = { playwright_options: '', __ui: { script_source: { is_generated_script: false, file_name: '' }, - is_zip_url_tls_enabled: false, is_tls_enabled: false, }, params: @@ -1221,11 +1138,6 @@ const browserConfig: any = { 'source.inline.script': 'step("Visit /users api route", async () => {\\n const response = await page.goto(\'https://nextjs-test-synthetics.vercel.app/api/users\');\\n expect(response.status()).toEqual(200);\\n});', 'source.project.content': '', - 'source.zip_url.url': '', - 'source.zip_url.username': '', - 'source.zip_url.password': '', - 'source.zip_url.folder': '', - 'source.zip_url.proxy_url': '', playwright_text_assertion: '', urls: '', screenshots: 'on', diff --git a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_meta_data.ts b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_meta_data.ts index da3ce0fab6021..5ef3c448e7e84 100644 --- a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_meta_data.ts +++ b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_meta_data.ts @@ -14,7 +14,6 @@ const ScriptSourceCodec = t.interface({ export const MetadataCodec = t.partial({ is_tls_enabled: t.boolean, - is_zip_url_tls_enabled: t.boolean, script_source: ScriptSourceCodec, }); diff --git a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts index 5cb78f45e1fdd..2e639e360fd1a 100644 --- a/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts +++ b/x-pack/plugins/synthetics/common/runtime_types/monitor_management/monitor_types.ts @@ -48,23 +48,6 @@ export const TLSCodec = t.intersection([TLSFieldsCodec, TLSSensitiveFieldsCodec] export type TLSFields = t.TypeOf; -// ZipUrlTLSFields -export const ZipUrlTLSFieldsCodec = t.partial({ - [ConfigKey.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: t.string, - [ConfigKey.ZIP_URL_TLS_CERTIFICATE]: t.string, - [ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE]: VerificationModeCodec, - [ConfigKey.ZIP_URL_TLS_VERSION]: t.array(TLSVersionCodec), -}); - -export const ZipUrlTLSSensitiveFieldsCodec = t.partial({ - [ConfigKey.ZIP_URL_TLS_KEY]: t.string, - [ConfigKey.ZIP_URL_TLS_KEY_PASSPHRASE]: t.string, -}); - -export const ZipUrlTLSCodec = t.intersection([ZipUrlTLSFieldsCodec, ZipUrlTLSSensitiveFieldsCodec]); - -export type ZipUrlTLSFields = t.TypeOf; - // CommonFields export const CommonFieldsCodec = t.intersection([ t.interface({ @@ -222,17 +205,12 @@ export const EncryptedBrowserSimpleFieldsCodec = t.intersection([ t.intersection([ t.interface({ [ConfigKey.METADATA]: MetadataCodec, - [ConfigKey.SOURCE_ZIP_URL]: t.string, - [ConfigKey.SOURCE_ZIP_FOLDER]: t.string, - [ConfigKey.SOURCE_ZIP_PROXY_URL]: t.string, }), t.partial({ [ConfigKey.PLAYWRIGHT_OPTIONS]: t.string, [ConfigKey.TEXT_ASSERTION]: t.string, }), ]), - ZipUrlTLSFieldsCodec, - ZipUrlTLSSensitiveFieldsCodec, CommonFieldsCodec, ]); @@ -240,13 +218,10 @@ export const BrowserSensitiveSimpleFieldsCodec = t.intersection([ t.interface({ [ConfigKey.SOURCE_INLINE]: t.string, [ConfigKey.SOURCE_PROJECT_CONTENT]: t.string, - [ConfigKey.SOURCE_ZIP_USERNAME]: t.string, - [ConfigKey.SOURCE_ZIP_PASSWORD]: t.string, [ConfigKey.PARAMS]: t.string, [ConfigKey.URLS]: t.union([t.string, t.null]), [ConfigKey.PORT]: t.union([t.number, t.null]), }), - ZipUrlTLSFieldsCodec, CommonFieldsCodec, ]); @@ -265,7 +240,6 @@ export const EncryptedBrowserAdvancedFieldsCodec = t.interface({ export const BrowserSimpleFieldsCodec = t.intersection([ EncryptedBrowserSimpleFieldsCodec, BrowserSensitiveSimpleFieldsCodec, - ZipUrlTLSSensitiveFieldsCodec, ]); export const BrowserSensitiveAdvancedFieldsCodec = t.interface({ diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/overview_search.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/overview_search.journey.ts index cb9983e830406..49bacee80380e 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/overview_search.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/overview_search.journey.ts @@ -68,32 +68,38 @@ journey('Overview Search', async ({ page, params }) => { await page.waitForSelector(`text=${elasticJourney}`); await page.waitForSelector(`text=${cnnJourney}`); await page.waitForSelector(`text=${googleJourney}`); - await page.focus('[data-test-subj="syntheticsOverviewSearchInput"]'); - await page.type('[data-test-subj="syntheticsOverviewSearchInput"]', 'Elastic', { delay: 300 }); - await page.waitForSelector(`text=${elasticJourney}`); - expect(await elastic.count()).toBe(1); - expect(await cnn.count()).toBe(0); - expect(await google.count()).toBe(0); - await page.click('[aria-label="Clear input"]'); - await page.type('[data-test-subj="syntheticsOverviewSearchInput"]', 'cnn', { delay: 300 }); - await page.waitForSelector(`text=${cnnJourney}`); - expect(await elastic.count()).toBe(0); - expect(await cnn.count()).toBe(1); - expect(await google.count()).toBe(0); - await page.click('[aria-label="Clear input"]'); - await page.type('[data-test-subj="syntheticsOverviewSearchInput"]', 'GOOGLE', { delay: 300 }); - await page.waitForSelector(`text=${googleJourney}`); - expect(await elastic.count()).toBe(0); - expect(await cnn.count()).toBe(0); - expect(await google.count()).toBe(1); - await page.click('[aria-label="Clear input"]'); - await page.type('[data-test-subj="syntheticsOverviewSearchInput"]', 'Journey', { delay: 300 }); - await page.waitForSelector(`text=${elasticJourney}`); - await page.waitForSelector(`text=${cnnJourney}`); - await page.waitForSelector(`text=${googleJourney}`); - expect(await elastic.count()).toBe(1); - expect(await cnn.count()).toBe(1); - expect(await google.count()).toBe(1); + await retry.tryForTime(60 * 1000, async () => { + await page.focus('[data-test-subj="syntheticsOverviewSearchInput"]'); + await page.type('[data-test-subj="syntheticsOverviewSearchInput"]', 'Elastic', { + delay: 300, + }); + await page.waitForSelector(`text=${elasticJourney}`); + expect(await elastic.count()).toBe(1); + expect(await cnn.count()).toBe(0); + expect(await google.count()).toBe(0); + await page.click('[aria-label="Clear input"]'); + await page.type('[data-test-subj="syntheticsOverviewSearchInput"]', 'cnn', { delay: 300 }); + await page.waitForSelector(`text=${cnnJourney}`); + expect(await elastic.count()).toBe(0); + expect(await cnn.count()).toBe(1); + expect(await google.count()).toBe(0); + await page.click('[aria-label="Clear input"]'); + await page.type('[data-test-subj="syntheticsOverviewSearchInput"]', 'GOOGLE', { delay: 300 }); + await page.waitForSelector(`text=${googleJourney}`); + expect(await elastic.count()).toBe(0); + expect(await cnn.count()).toBe(0); + expect(await google.count()).toBe(1); + await page.click('[aria-label="Clear input"]'); + await page.type('[data-test-subj="syntheticsOverviewSearchInput"]', 'Journey', { + delay: 300, + }); + await page.waitForSelector(`text=${elasticJourney}`); + await page.waitForSelector(`text=${cnnJourney}`); + await page.waitForSelector(`text=${googleJourney}`); + expect(await elastic.count()).toBe(1); + expect(await cnn.count()).toBe(1); + expect(await google.count()).toBe(1); + }); }); step('searches by tags', async () => { diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/services/add_monitor.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/services/add_monitor.ts index 90a16752441ef..44053aa29ed26 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/services/add_monitor.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/services/add_monitor.ts @@ -117,18 +117,12 @@ export const testDataMonitor = { playwright_options: '', __ui: { script_source: { is_generated_script: false, file_name: '' }, - is_zip_url_tls_enabled: false, }, params: '', 'url.port': null, 'source.inline.script': "step('Go to https://www.google.com', async () => {\n await page.goto('https://www.google.com');\n expect(await page.isVisible('text=Data')).toBeTruthy();\n });", 'source.project.content': '', - 'source.zip_url.url': '', - 'source.zip_url.username': '', - 'source.zip_url.password': '', - 'source.zip_url.folder': '', - 'source.zip_url.proxy_url': '', playwright_text_assertion: 'Data', urls: 'https://www.google.com', screenshots: 'on', diff --git a/x-pack/plugins/synthetics/e2e/tasks/import_monitors.ts b/x-pack/plugins/synthetics/e2e/tasks/import_monitors.ts index 2d28655da4e1b..f65d82a9933f4 100644 --- a/x-pack/plugins/synthetics/e2e/tasks/import_monitors.ts +++ b/x-pack/plugins/synthetics/e2e/tasks/import_monitors.ts @@ -35,7 +35,6 @@ export const importMonitors = async ({ playwright_options: '', __ui: { script_source: { is_generated_script: false, file_name: '' }, - is_zip_url_tls_enabled: false, is_tls_enabled: false, }, params: '', @@ -43,11 +42,6 @@ export const importMonitors = async ({ 'source.inline.script': "const username = 'diawar.khan.shewani+conduit@gmail.com';\nconst password = 'aNL2sTGRbNYauc8';\n// Goto https://demo.realworld.io/ and sign up for username and password\n\nconst articleTitle = 'Artile No. ' + Math.ceil(Math.random() * 1000);\n\nstep(\"Goto home page\", async () => {\n await page.goto('https://demo.realworld.io/');\n});\n\nstep(\"Goto login page\", async () => {\n await page.click('text=Sign in');\n});\n\nstep(\"Enter login credentials\", async () => {\n await page.fill('[placeholder=\"Email\"]', username);\n await page.fill('[placeholder=\"Password\"]', password);\n});\n\nstep(\"Sign in\", async () => {\n await page.click('button[type=submit]');\n});\n\nstep(\"Create article\", async () => {\n const articleSubject = 'Test article subject';\n const articleBody = 'This ariticle is created with **synthetics** for purely testing purposes.';\n\n await page.click('text=New Article');\n await page.fill('[placeholder=\"Article Title\"]', articleTitle);\n await page.fill('[placeholder=\"What\\'s this article about?\"]', articleSubject);\n await page.fill('textarea', articleBody);\n});\n\nstep(\"Publish article\", async () => {\n await page.click('text=Publish Article');\n await page.waitForNavigation();\n\n // Fail about 30% of random times\n const passFailText = Math.random() * 10 > 7 ? 'non-existent-text' : articleTitle ;\n await page.waitForSelector('text=' + articleTitle);\n});\n\nstep(\"Post 1st comment\", async() => {\n const firstCommentText = 'First comment!';\n await page.fill('[placeholder=\"Write a comment...\"]', firstCommentText);\n await page.click('text=Post Comment');\n await page.waitForSelector('text=' + firstCommentText);\n});", 'source.project.content': '', - 'source.zip_url.url': '', - 'source.zip_url.username': '', - 'source.zip_url.password': '', - 'source.zip_url.folder': '', - 'source.zip_url.proxy_url': '', urls: '', screenshots: 'on', synthetics_args: [], diff --git a/x-pack/plugins/synthetics/e2e/tasks/uptime_monitor.ndjson b/x-pack/plugins/synthetics/e2e/tasks/uptime_monitor.ndjson index bb8acca240094..818bbfcefd579 100644 --- a/x-pack/plugins/synthetics/e2e/tasks/uptime_monitor.ndjson +++ b/x-pack/plugins/synthetics/e2e/tasks/uptime_monitor.ndjson @@ -1,2 +1,2 @@ -{"attributes":{"__ui":{"is_tls_enabled":false,"is_zip_url_tls_enabled":false},"check.request.method":"GET","check.response.status":[],"enabled":true,"locations":[{"geo":{"lat":41.25,"lon":-95.86},"id":"us_central","label":"US Central","url":"https://us-central.synthetics.elastic.dev"}],"max_redirects":"0","name":"Test Monitor","proxy_url":"","response.include_body":"on_error","response.include_headers":true,"schedule":{"number":"3","unit":"m"},"service.name":"","tags":[],"timeout":"16","type":"http","urls":"https://www.google.com", "secrets": "{}"},"coreMigrationVersion":"8.1.0","id":"832b9980-7fba-11ec-b360-25a79ce3f496","references":[],"sort":[1643319958480,20371],"type":"synthetics-monitor","updated_at":"2022-01-27T21:45:58.480Z","version":"WzExOTg3ODYsMl0="} +{"attributes":{"__ui":{"is_tls_enabled":false},"check.request.method":"GET","check.response.status":[],"enabled":true,"locations":[{"geo":{"lat":41.25,"lon":-95.86},"id":"us_central","label":"US Central","url":"https://us-central.synthetics.elastic.dev"}],"max_redirects":"0","name":"Test Monitor","proxy_url":"","response.include_body":"on_error","response.include_headers":true,"schedule":{"number":"3","unit":"m"},"service.name":"","tags":[],"timeout":"16","type":"http","urls":"https://www.google.com", "secrets": "{}"},"coreMigrationVersion":"8.1.0","id":"832b9980-7fba-11ec-b360-25a79ce3f496","references":[],"sort":[1643319958480,20371],"type":"synthetics-monitor","updated_at":"2022-01-27T21:45:58.480Z","version":"WzExOTg3ODYsMl0="} {"excludedObjects":[],"excludedObjectsCount":0,"exportedCount":2,"missingRefCount":0,"missingReferences":[]} diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_location_select.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_location_select.tsx index afbabe76db571..a4b70e0704b66 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_location_select.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/monitor_location_select.tsx @@ -79,6 +79,7 @@ export const MonitorLocationSelect = ({ closeLocationList(); onChange(location.id, location.label); }} + disabled={selectedLocation?.id === location.id} > {location.label} diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/page_loader.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/page_loader.tsx new file mode 100644 index 0000000000000..10ab9848c6c0b --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/page_loader.tsx @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { EuiFlexGroup, EuiFlexItem, EuiTitle, EuiSpacer, EuiText } from '@elastic/eui'; + +export const PageLoader = ({ + title, + body, + icon, +}: { + title: React.ReactElement; + body?: React.ReactElement; + icon: React.ReactElement; +}) => { + return ( + + + {icon} + + {title} + + {body && {body}} + + + ); +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/defaults.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/defaults.test.tsx index 47c1ce7989ef0..70521c28983f7 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/defaults.test.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/defaults.test.tsx @@ -44,17 +44,6 @@ describe('defaults', () => { 'service.name': '', 'source.inline.script': testScript, 'source.project.content': '', - 'source.zip_url.folder': '', - 'source.zip_url.password': '', - 'source.zip_url.proxy_url': '', - 'source.zip_url.ssl.certificate': undefined, - 'source.zip_url.ssl.certificate_authorities': undefined, - 'source.zip_url.ssl.key': undefined, - 'source.zip_url.ssl.key_passphrase': undefined, - 'source.zip_url.ssl.supported_protocols': undefined, - 'source.zip_url.ssl.verification_mode': undefined, - 'source.zip_url.url': '', - 'source.zip_url.username': '', 'ssl.certificate': '', 'ssl.certificate_authorities': '', 'ssl.key': '', @@ -117,17 +106,6 @@ describe('defaults', () => { }, 'source.inline.script': 'testScript', 'source.project.content': '', - 'source.zip_url.folder': '', - 'source.zip_url.password': '', - 'source.zip_url.proxy_url': '', - 'source.zip_url.ssl.certificate': undefined, - 'source.zip_url.ssl.certificate_authorities': undefined, - 'source.zip_url.ssl.key': undefined, - 'source.zip_url.ssl.key_passphrase': undefined, - 'source.zip_url.ssl.supported_protocols': undefined, - 'source.zip_url.ssl.verification_mode': undefined, - 'source.zip_url.url': '', - 'source.zip_url.username': '', 'ssl.certificate': '', 'ssl.certificate_authorities': '', 'ssl.key': '', diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx index 110ac698e22f7..934a895c47d34 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx @@ -68,7 +68,11 @@ import { FieldMap, FormLocation, } from '../types'; -import { AlertConfigKey, DEFAULT_BROWSER_ADVANCED_FIELDS } from '../constants'; +import { + AlertConfigKey, + DEFAULT_BROWSER_ADVANCED_FIELDS, + ALLOWED_SCHEDULES_IN_MINUTES, +} from '../constants'; import { getDefaultFormFields } from './defaults'; import { validate, validateHeaders, WHOLE_NUMBERS_ONLY, FLOATS_ONLY } from './validation'; @@ -90,16 +94,10 @@ const getScheduleContent = (value: number) => { } }; -const getScheduleConfig = (schedules: number[]) => { - return schedules.map((value) => ({ - value: `${value}`, - text: getScheduleContent(value), - })); -}; - -const BROWSER_SCHEDULES = getScheduleConfig([3, 5, 10, 15, 30, 60, 120, 240]); - -const LIGHTWEIGHT_SCHEDULES = getScheduleConfig([1, 3, 5, 10, 15, 30, 60]); +const SCHEDULES = ALLOWED_SCHEDULES_IN_MINUTES.map((value) => ({ + value, + text: getScheduleContent(parseInt(value, 10)), +})); export const MONITOR_TYPE_CONFIG = { [FormMonitorType.MULTISTEP]: { @@ -378,12 +376,10 @@ export const FIELD = (readOnly?: boolean): FieldMap => ({ defaultMessage: 'How often do you want to run this test? Higher frequencies will increase your total cost.', }), - dependencies: [ConfigKey.MONITOR_TYPE], - props: ({ dependencies }): EuiSelectProps => { - const [monitorType] = dependencies; + props: (): EuiSelectProps => { return { 'data-test-subj': 'syntheticsMonitorConfigSchedule', - options: monitorType === DataStream.BROWSER ? BROWSER_SCHEDULES : LIGHTWEIGHT_SCHEDULES, + options: SCHEDULES, disabled: readOnly, }; }, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/formatter.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/formatter.test.tsx index 6a457d601802b..6000b02cb7f87 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/formatter.test.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/formatter.test.tsx @@ -188,16 +188,10 @@ describe('format', () => { is_generated_script: false, file_name: '', }, - is_zip_url_tls_enabled: false, }, params: '', 'source.inline.script': '', 'source.project.content': '', - 'source.zip_url.url': '', - 'source.zip_url.username': '', - 'source.zip_url.password': '', - 'source.zip_url.folder': '', - 'source.zip_url.proxy_url': '', playwright_text_assertion: '', urls: '', screenshots: 'on', @@ -276,17 +270,6 @@ describe('format', () => { 'service.name': '', 'source.inline.script': script, 'source.project.content': '', - 'source.zip_url.folder': '', - 'source.zip_url.password': '', - 'source.zip_url.proxy_url': '', - 'source.zip_url.ssl.certificate': undefined, - 'source.zip_url.ssl.certificate_authorities': undefined, - 'source.zip_url.ssl.key': undefined, - 'source.zip_url.ssl.key_passphrase': undefined, - 'source.zip_url.ssl.supported_protocols': undefined, - 'source.zip_url.ssl.verification_mode': undefined, - 'source.zip_url.url': '', - 'source.zip_url.username': '', 'ssl.certificate': '', 'ssl.certificate_authorities': '', 'ssl.key': '', diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/validation.test.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/validation.test.ts index fa91bd457671d..4b4e524c9f3b0 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/validation.test.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/validation.test.ts @@ -49,10 +49,7 @@ describe('[Monitor Management] validation', () => { }); }); - describe.each([ - [ConfigKey.SOURCE_INLINE, 'step(() => {});'], - [ConfigKey.SOURCE_ZIP_URL, 'https://test.zip'], - ])('Browser', (configKey, value) => { + describe.each([[ConfigKey.SOURCE_INLINE, 'step(() => {});']])('Browser', (configKey, value) => { const browserProps: Partial = { ...commonPropsValid, [ConfigKey.MONITOR_TYPE]: DataStream.BROWSER, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/validation.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/validation.tsx index 9f06395bf1a19..cc03228d755be 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/validation.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/validation.tsx @@ -147,14 +147,7 @@ const validateThrottleValue = (speed: string | undefined, allowZero?: boolean) = const validateBrowser: ValidationLibrary = { ...validateCommon, - [ConfigKey.SOURCE_ZIP_URL]: ({ - [ConfigKey.SOURCE_ZIP_URL]: zipUrl, - [ConfigKey.SOURCE_INLINE]: inlineScript, - }) => !zipUrl && !inlineScript, - [ConfigKey.SOURCE_INLINE]: ({ - [ConfigKey.SOURCE_ZIP_URL]: zipUrl, - [ConfigKey.SOURCE_INLINE]: inlineScript, - }) => !zipUrl && !inlineScript, + [ConfigKey.SOURCE_INLINE]: ({ [ConfigKey.SOURCE_INLINE]: inlineScript }) => !inlineScript, [ConfigKey.DOWNLOAD_SPEED]: ({ [ConfigKey.DOWNLOAD_SPEED]: downloadSpeed }) => validateThrottleValue(downloadSpeed), [ConfigKey.UPLOAD_SPEED]: ({ [ConfigKey.UPLOAD_SPEED]: uploadSpeed }) => diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_monitor_latest_ping.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_monitor_latest_ping.tsx index 6ad6931694b38..77c4e813f654c 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_monitor_latest_ping.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_monitor_latest_ping.tsx @@ -28,7 +28,7 @@ export const useMonitorLatestPing = (params?: UseMonitorLatestPingParams) => { const monitorId = params?.monitorId ?? monitor?.id; const locationLabel = params?.locationLabel ?? location?.label; - const { data: latestPing, loading } = useSelector(selectLastRunMetadata); + const { data: latestPing, loading, loaded } = useSelector(selectLastRunMetadata); const latestPingId = latestPing?.monitor.id; @@ -46,16 +46,16 @@ export const useMonitorLatestPing = (params?: UseMonitorLatestPingParams) => { }, [dispatch, monitorId, locationLabel, isUpToDate, lastRefresh]); if (!monitorId || !locationLabel) { - return { loading, latestPing: undefined }; + return { loading, latestPing: undefined, loaded }; } if (!latestPing) { - return { loading, latestPing: undefined }; + return { loading, latestPing: undefined, loaded }; } if (!isIdSame || !isLocationSame) { - return { loading, latestPing: undefined }; + return { loading, latestPing: undefined, loaded }; } - return { loading, latestPing }; + return { loading, latestPing, loaded }; }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_errors/monitor_errors.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_errors/monitor_errors.tsx index 04930af018152..2a8ac3cd3691e 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_errors/monitor_errors.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_errors/monitor_errors.tsx @@ -19,6 +19,7 @@ import { useMonitorDetailsPage } from '../use_monitor_details_page'; import { useMonitorErrors } from '../hooks/use_monitor_errors'; import { SyntheticsDatePicker } from '../../common/date_picker/synthetics_date_picker'; import { ErrorsTabContent } from './errors_tab_content'; +import { MonitorPendingWrapper } from '../monitor_pending_wrapper'; export const MonitorErrors = () => { const { errorStates, loading, data } = useMonitorErrors(); @@ -33,7 +34,7 @@ export const MonitorErrors = () => { } return ( - <> + {initialLoading && } @@ -41,7 +42,7 @@ export const MonitorErrors = () => {
- +
); }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_history/monitor_history.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_history/monitor_history.tsx index 5dd0570cde7e3..4d9fb7bc6e0d9 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_history/monitor_history.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_history/monitor_history.tsx @@ -24,6 +24,7 @@ import { DurationSparklines } from '../monitor_summary/duration_sparklines'; import { MonitorCompleteSparklines } from '../monitor_summary/monitor_complete_sparklines'; import { MonitorStatusPanel } from '../monitor_status/monitor_status_panel'; import { useMonitorQueryId } from '../hooks/use_monitor_query_id'; +import { MonitorPendingWrapper } from '../monitor_pending_wrapper'; const STATS_WIDTH_SINGLE_COLUMN_THRESHOLD = 360; // ✨ determined by trial and error @@ -48,107 +49,109 @@ export const MonitorHistory = () => { } return ( - - - - - - - - {/* @ts-expect-error Current @elastic/eui has the wrong types for the ref */} - - -

{STATS_LABEL}

-
- - - - - - - - - - - - - - - - - - - - - - - - - {monitorId && ( - + + + + + + + + {/* @ts-expect-error Current @elastic/eui has the wrong types for the ref */} + + +

{STATS_LABEL}

+
+ + + + + + + + + + + + + + + + + + - )} - - - {monitorId && ( - - )} - - - - - - - - - - - - - - - - - -
-
- - - -

{DURATION_TREND_LABEL}

-
- -
-
-
-
- - - - - - -
+
+
+
+ + + + {monitorId && ( + + )} + + + {monitorId && ( + + )} + + + + + + + + + + + + + + + + +
+
+
+ + + +

{DURATION_TREND_LABEL}

+
+ +
+
+
+
+ + + + + + +
+ ); }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_pending_wrapper.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_pending_wrapper.test.tsx new file mode 100644 index 0000000000000..531ef23c41159 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_pending_wrapper.test.tsx @@ -0,0 +1,115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render } from '../../utils/testing/rtl_helpers'; +import { MonitorPendingWrapper } from './monitor_pending_wrapper'; +import * as selectedMonitorHooks from './hooks/use_selected_monitor'; +import * as locationHooks from './hooks/use_selected_location'; + +describe('MonitorPendingWrapper', () => { + const TestComponent = () => { + return
children
; + }; + + beforeEach(() => { + jest.clearAllMocks(); + jest.spyOn(selectedMonitorHooks, 'useSelectedMonitor').mockReturnValue({ + monitor: { + id: '4afd3980-0b72-11ed-9c10-b57918ea89d6', + }, + } as ReturnType); + jest.spyOn(locationHooks, 'useSelectedLocation').mockReturnValue({ + label: 'North America - US Central', + } as ReturnType); + }); + + it('displays loading when initial ping is loading', async () => { + const { getByText, queryByText, getByTestId } = render( + + + , + { + state: { + monitorDetails: { + lastRun: { + loaded: false, + loading: true, + data: undefined, + }, + }, + }, + } + ); + + // page is loading + expect(getByText(/Loading/)).toBeInTheDocument(); + expect(queryByText(/Initial test run pending/)).not.toBeInTheDocument(); + expect(getByTestId('syntheticsPendingWrapperChildren')).toHaveAttribute( + 'style', + 'display: none;' + ); + }); + + it('displays pending when latest ping is unavailable', async () => { + const { getByText, queryByText, getByTestId } = render( + + + , + { + state: { + monitorDetails: { + lastRun: { + loaded: true, + loading: false, + // overwrite default from + // merged properties for default + // mock state + // @ts-ignore + data: null, + }, + }, + }, + } + ); + + // page is loaded with pending run + expect(queryByText(/Loading/)).not.toBeInTheDocument(); + expect(getByTestId('syntheticsPendingWrapperChildren')).toHaveAttribute( + 'style', + 'display: none;' + ); + expect(getByText(/Initial test run pending/)).toBeInTheDocument(); + }); + + it('displays children when latestPing is available', async () => { + const { queryByText, getByTestId } = render( + + + , + { + state: { + monitorDetails: { + lastRun: { + loaded: true, + loading: false, + data: {}, + }, + }, + }, + } + ); + + // page is loaded with latest ping defined + expect(queryByText(/Loading/)).not.toBeInTheDocument(); + expect(queryByText(/Initial test run pending/)).not.toBeInTheDocument(); + expect(getByTestId('syntheticsPendingWrapperChildren')).not.toHaveAttribute( + 'style', + 'display: none;' + ); + }); +}); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_pending_wrapper.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_pending_wrapper.tsx new file mode 100644 index 0000000000000..7ff27c67384e1 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_pending_wrapper.tsx @@ -0,0 +1,108 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect, useState, useMemo, useRef } from 'react'; +import { useHistory, useParams, useLocation } from 'react-router-dom'; +import { useDispatch } from 'react-redux'; +import { i18n } from '@kbn/i18n'; +import { EuiLoadingSpinner, EuiLoadingChart } from '@elastic/eui'; +import { PageLoader } from '../common/components/page_loader'; +import { resetMonitorLastRunAction } from '../../state'; +import { useMonitorLatestPing } from './hooks/use_monitor_latest_ping'; + +export const MonitorPendingWrapper: React.FC = ({ children }) => { + const dispatch = useDispatch(); + const history = useHistory(); + const currentLocation = useLocation(); + const locationRef = useRef(currentLocation); + const { monitorId } = useParams<{ monitorId: string }>(); + + const { latestPing, loaded: pingsLoaded } = useMonitorLatestPing(); + const [loaded, setLoaded] = useState(false); + const [hasPing, setHasPing] = useState(false); + + const unlisten = useMemo( + () => + history.listen((location) => { + const currentMonitorId = location.pathname.split('/')[2] || ''; + const hasDifferentSearch = locationRef.current.search !== location.search; + const hasDifferentId = currentMonitorId !== monitorId; + locationRef.current = location; + if (hasDifferentSearch || hasDifferentId) { + setLoaded(false); + setHasPing(false); + dispatch(resetMonitorLastRunAction()); + } + }), + [history, monitorId, dispatch] + ); + + useEffect(() => { + return function cleanup() { + unlisten(); + }; + }, [unlisten]); + + useEffect(() => { + if (pingsLoaded) { + setLoaded(true); + } + if (pingsLoaded && latestPing) { + setHasPing(true); + } + }, [pingsLoaded, latestPing, dispatch, unlisten]); + + return ( + <> + {!loaded ? ( + } + title={

{LOADING_TITLE}

} + body={

{LOADING_DESCRIPTION}

} + /> + ) : null} + {loaded && !hasPing ? ( + } + title={

{MONITOR_PENDING_HEADING}

} + body={

{MONITOR_PENDING_CONTENT}

} + /> + ) : null} +
+ {children} +
+ + ); +}; + +export const MONITOR_PENDING_HEADING = i18n.translate( + 'xpack.synthetics.monitorDetails.pending.heading', + { + defaultMessage: 'Initial test run pending...', + } +); + +export const MONITOR_PENDING_CONTENT = i18n.translate( + 'xpack.synthetics.monitorDetails.pending.content', + { + defaultMessage: 'This page will refresh when data becomes available.', + } +); + +export const LOADING_DESCRIPTION = i18n.translate( + 'xpack.synthetics.monitorDetails.loading.content', + { + defaultMessage: 'This will take just a second.', + } +); + +export const LOADING_TITLE = i18n.translate('xpack.synthetics.monitorDetails.loading.heading', { + defaultMessage: 'Loading monitor details', +}); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/monitor_summary.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/monitor_summary.tsx index ebaaee6e44e50..bd840fb92f718 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/monitor_summary.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/monitor_summary.tsx @@ -26,6 +26,7 @@ import { AvailabilitySparklines } from './availability_sparklines'; import { LastTestRun } from './last_test_run'; import { LAST_10_TEST_RUNS, TestRunsTable } from './test_runs_table'; import { MonitorErrorsCount } from './monitor_errors_count'; +import { MonitorPendingWrapper } from '../monitor_pending_wrapper'; export const MonitorSummary = () => { const { from, to } = useMonitorRangeFrom(); @@ -40,7 +41,7 @@ export const MonitorSummary = () => { } return ( - <> + @@ -138,7 +139,7 @@ export const MonitorSummary = () => { - + ); }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/loader/loader.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/loader/loader.tsx index b40fb64a39576..4ca7ec2a835e3 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/loader/loader.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/loader/loader.tsx @@ -6,7 +6,8 @@ */ import React from 'react'; -import { EuiEmptyPrompt, EuiLoadingLogo, EuiSpacer } from '@elastic/eui'; +import { EuiEmptyPrompt, EuiLoadingSpinner, EuiSpacer } from '@elastic/eui'; +import { PageLoader } from '../../../common/components/page_loader'; interface Props { loading: boolean; @@ -40,12 +41,7 @@ export const Loader = ({ ) : null} {loading ? ( - } - title={

{loadingTitle}

} - data-test-subj="uptimeLoader" - /> + } title={

{loadingTitle}

} /> ) : null} ); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/actions.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/actions.ts index ae6e7ff8933f7..195c2b1fcb400 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/actions.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/actions.ts @@ -27,6 +27,8 @@ export const getMonitorLastRunAction = createAsyncAction< PingsResponse >('[MONITOR DETAILS] GET LAST RUN'); +export const resetMonitorLastRunAction = createAction('[MONITOR DETAILS] LAST RUN RESET'); + export const updateMonitorLastRunAction = createAction<{ data: Ping }>( '[MONITOR DETAILS] UPdATE LAST RUN' ); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/index.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/index.ts index ec1abe5401dc2..2cdd1eadcb27f 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/index.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/monitor_details/index.ts @@ -15,6 +15,7 @@ import { IHttpSerializedFetchError } from '../utils/http_error'; import { getMonitorLastRunAction, updateMonitorLastRunAction, + resetMonitorLastRunAction, getMonitorRecentPingsAction, setMonitorDetailsLocationAction, getMonitorAction, @@ -29,6 +30,7 @@ export interface MonitorDetailsState { lastRun: { data?: Ping; loading: boolean; + loaded: boolean; }; syntheticsMonitorLoading: boolean; syntheticsMonitor: EncryptedSyntheticsSavedMonitor | null; @@ -39,7 +41,7 @@ export interface MonitorDetailsState { const initialState: MonitorDetailsState = { pings: { total: 0, data: [], loading: false }, - lastRun: { loading: false }, + lastRun: { loading: false, loaded: false }, syntheticsMonitor: null, syntheticsMonitorLoading: false, syntheticsMonitorDispatchedAt: 0, @@ -54,12 +56,14 @@ export const monitorDetailsReducer = createReducer(initialState, (builder) => { }) .addCase(getMonitorLastRunAction.get, (state, action) => { state.lastRun.loading = true; + state.lastRun.loaded = false; if (checkIsStalePing(action.payload.monitorId, state.lastRun.data)) { state.lastRun.data = undefined; } }) .addCase(getMonitorLastRunAction.success, (state, action) => { state.lastRun.loading = false; + state.lastRun.loaded = true; state.lastRun.data = action.payload.pings[0]; }) .addCase(getMonitorLastRunAction.fail, (state, action) => { @@ -69,6 +73,9 @@ export const monitorDetailsReducer = createReducer(initialState, (builder) => { .addCase(updateMonitorLastRunAction, (state, action) => { state.lastRun.data = action.payload.data; }) + .addCase(resetMonitorLastRunAction, (state, action) => { + state.lastRun.loaded = false; + }) .addCase(getMonitorRecentPingsAction.get, (state, action) => { state.pings.loading = true; state.pings.data = state.pings.data.filter( diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts index 051f0731c62e6..d65c68f2843b9 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts @@ -175,6 +175,7 @@ function getMonitorDetailsMockSlice() { return { lastRun: { loading: false, + loaded: true, data: { summary: { up: 1, down: 0 }, agent: { @@ -416,7 +417,6 @@ function getMonitorDetailsMockSlice() { playwright_options: '', __ui: { script_source: { is_generated_script: false, file_name: '' }, - is_zip_url_tls_enabled: false, is_tls_enabled: false, }, params: '', @@ -424,11 +424,6 @@ function getMonitorDetailsMockSlice() { 'source.inline.script': "step('Goto one pixel image', async () => {\\n await page.goto('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==');\\n});", 'source.project.content': '', - 'source.zip_url.url': '', - 'source.zip_url.username': '', - 'source.zip_url.password': '', - 'source.zip_url.folder': '', - 'source.zip_url.proxy_url': '', urls: 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==', screenshots: 'on', synthetics_args: [], diff --git a/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.test.ts b/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.test.ts index d95883f78b7b1..367c27c4bca08 100644 --- a/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.test.ts +++ b/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.test.ts @@ -161,12 +161,8 @@ const testMonitors = [ playwright_options: '', __ui: { script_source: { is_generated_script: false, file_name: '' }, - is_zip_url_tls_enabled: false, }, 'url.port': null, - 'source.zip_url.url': '', - 'source.zip_url.folder': '', - 'source.zip_url.proxy_url': '', playwright_text_assertion: '', urls: 'https://www.google.com', screenshots: 'on', diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/8.6.0.test.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/8.6.0.test.ts index 68e3416174c8f..3f046a5ed3115 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/8.6.0.test.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/8.6.0.test.ts @@ -37,7 +37,7 @@ const monitor850UI = { origin: 'ui', journey_id: '', id: '', - __ui: { is_tls_enabled: false, is_zip_url_tls_enabled: false }, + __ui: { is_tls_enabled: false }, urls: 'https://elastic.co', max_redirects: '0', 'url.port': null, diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/8.6.0.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/8.6.0.ts index 1302e3bc203b8..cce87ec58f649 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/8.6.0.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/8.6.0.ts @@ -7,7 +7,7 @@ import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server'; import { SavedObjectUnsanitizedDoc } from '@kbn/core/server'; import { ConfigKey, SyntheticsMonitorWithSecrets } from '../../../../../../common/runtime_types'; -import { SYNTHETICS_MONITOR_ENCRYPTED_TYPE } from '../../synthetics_monitor'; +import { LEGACY_SYNTHETICS_MONITOR_ENCRYPTED_TYPE } from '../../synthetics_monitor'; export const migration860 = (encryptedSavedObjects: EncryptedSavedObjectsPluginSetup) => { return encryptedSavedObjects.createMigration< @@ -32,6 +32,6 @@ export const migration860 = (encryptedSavedObjects: EncryptedSavedObjectsPluginS }, }; }, - migratedType: SYNTHETICS_MONITOR_ENCRYPTED_TYPE, + migratedType: LEGACY_SYNTHETICS_MONITOR_ENCRYPTED_TYPE, }); }; diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/8.8.0.test.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/8.8.0.test.ts new file mode 100644 index 0000000000000..229a45eae14c5 --- /dev/null +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/8.8.0.test.ts @@ -0,0 +1,382 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks'; +import { migration880 } from './8.8.0'; +import { migrationMocks } from '@kbn/core/server/mocks'; +import { ConfigKey, ScheduleUnit } from '../../../../../../common/runtime_types'; +import { ALLOWED_SCHEDULES_IN_MINUTES } from '../../../../../../common/constants/monitor_defaults'; +import { + browserUI, + browserProject, + browserUptimeUI, + tcpUptimeUI, + icmpUptimeUI, + httpUptimeUI, +} from './test_fixtures/8.7.0'; + +const context = migrationMocks.createContext(); +const encryptedSavedObjectsSetup = encryptedSavedObjectsMock.createSetup(); + +describe('Monitor migrations v8.7.0 -> v8.8.0', () => { + const testSchedules = [ + ['4', '3'], + ['7', '5'], + ['8', '10'], + ['9.5', '10'], + ['12', '10'], + ['13', '15'], + ['16', '15'], + ['18', '20'], + ['21', '20'], + ['25', '20'], + ['26', '30'], + ['31', '30'], + ['45', '30'], + ['46', '60'], + ['61', '60'], + ['90', '60'], + ['91', '120'], + ['121', '120'], + ['195', '240'], + ['600', '240'], + ]; + + beforeEach(() => { + jest.resetAllMocks(); + encryptedSavedObjectsSetup.createMigration.mockImplementation(({ migration }) => migration); + }); + + describe('config hash', () => { + it('sets config hash back to empty string', () => { + expect(browserProject.attributes[ConfigKey.CONFIG_HASH]).toBeTruthy(); + const actual = migration880(encryptedSavedObjectsSetup)(browserProject, context); + expect(actual.attributes[ConfigKey.CONFIG_HASH]).toEqual(''); + }); + }); + + describe('zip url deprecation', () => { + it('removes all top level zip url fields for synthetics UI monitor', () => { + expect( + Object.keys(browserUI.attributes).some((key: string) => key.includes('zip_url')) + ).toEqual(true); + const actual = migration880(encryptedSavedObjectsSetup)(browserUI, context); + expect(actual).toEqual({ + attributes: { + __ui: { + script_source: { + file_name: '', + is_generated_script: false, + }, + }, + alert: { + status: { + enabled: true, + }, + }, + config_id: '311cf324-2fc9-4453-9ba5-5e745fd81722', + enabled: true, + 'filter_journeys.match': '', + 'filter_journeys.tags': [], + form_monitor_type: 'multistep', + hash: '', + id: '311cf324-2fc9-4453-9ba5-5e745fd81722', + ignore_https_errors: false, + journey_id: '', + locations: [ + { + geo: { + lat: 41.25, + lon: -95.86, + }, + id: 'us_central', + isServiceManaged: true, + label: 'North America - US Central', + }, + ], + name: 'https://elastic.co', + namespace: 'default', + origin: 'ui', + playwright_options: '', + playwright_text_assertion: '', + project_id: '', + revision: 1, + schedule: { + number: '10', + unit: 'm', + }, + screenshots: 'on', + secrets: + '{"params":"","source.inline.script":"step(\'Go to https://elastic.co\', async () => {\\n await page.goto(\'https://elastic.co\');\\n});","source.project.content":"","synthetics_args":[],"ssl.key":"","ssl.key_passphrase":""}', + 'service.name': '', + 'ssl.certificate': '', + 'ssl.certificate_authorities': '', + 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], + 'ssl.verification_mode': 'full', + tags: [], + 'throttling.config': '5d/3u/20l', + 'throttling.download_speed': '5', + 'throttling.is_enabled': true, + 'throttling.latency': '20', + 'throttling.upload_speed': '3', + timeout: null, + type: 'browser', + 'url.port': null, + urls: 'https://elastic.co', + }, + coreMigrationVersion: '8.8.0', + created_at: '2023-03-31T20:31:24.177Z', + id: '311cf324-2fc9-4453-9ba5-5e745fd81722', + references: [], + type: 'synthetics-monitor', + typeMigrationVersion: '8.6.0', + updated_at: '2023-03-31T20:31:24.177Z', + }); + expect(Object.keys(actual.attributes).some((key: string) => key.includes('zip_url'))).toEqual( + false + ); + }); + + it.each([browserUptimeUI, browserProject])( + 'removes all top level zip url fields for Uptime and Project monitors', + (testMonitor) => { + expect( + Object.keys(testMonitor.attributes).some((key: string) => key.includes('zip_url')) + ).toEqual(true); + const actual = migration880(encryptedSavedObjectsSetup)(testMonitor, context); + expect( + Object.keys(actual.attributes).some((key: string) => key.includes('zip_url')) + ).toEqual(false); + } + ); + + it('returns the original doc if an error occurs removing zip url fields', () => { + const invalidTestMonitor = { + ...browserUI, + attributes: { + ...browserUI.attributes, + name: null, + }, + }; + // @ts-ignore specificially testing monitors with invalid values + const actual = migration880(encryptedSavedObjectsSetup)(invalidTestMonitor, context); + expect(actual).toEqual(invalidTestMonitor); + }); + }); + + describe('schedule migration', () => { + it.each(testSchedules)( + 'handles migrating schedule with invalid schedules - browser', + (previous, migrated) => { + const testMonitorWithSchedule = { + ...browserUptimeUI, + attributes: { + ...browserUptimeUI.attributes, + [ConfigKey.SCHEDULE]: { + unit: ScheduleUnit.MINUTES, + number: previous, + }, + }, + }; + expect( + ALLOWED_SCHEDULES_IN_MINUTES.includes( + testMonitorWithSchedule.attributes[ConfigKey.SCHEDULE].number + ) + ).toBe(false); + const actual = migration880(encryptedSavedObjectsSetup)(testMonitorWithSchedule, context); + expect(actual.attributes[ConfigKey.SCHEDULE].number).toEqual(migrated); + expect( + ALLOWED_SCHEDULES_IN_MINUTES.includes(actual.attributes[ConfigKey.SCHEDULE].number) + ).toBe(true); + expect(actual.attributes[ConfigKey.SCHEDULE].unit).toEqual(ScheduleUnit.MINUTES); + } + ); + + it.each(ALLOWED_SCHEDULES_IN_MINUTES)( + 'handles migrating schedule with valid schedules - browser', + (validSchedule) => { + const testMonitorWithSchedule = { + ...browserUptimeUI, + attributes: { + ...browserUptimeUI.attributes, + [ConfigKey.SCHEDULE]: { + unit: ScheduleUnit.MINUTES, + number: validSchedule, + }, + }, + }; + expect( + ALLOWED_SCHEDULES_IN_MINUTES.includes( + testMonitorWithSchedule.attributes[ConfigKey.SCHEDULE].number + ) + ).toBe(true); + const actual = migration880(encryptedSavedObjectsSetup)(testMonitorWithSchedule, context); + expect(actual.attributes[ConfigKey.SCHEDULE].number).toEqual(validSchedule); + expect( + ALLOWED_SCHEDULES_IN_MINUTES.includes(actual.attributes[ConfigKey.SCHEDULE].number) + ).toBe(true); + expect(actual.attributes[ConfigKey.SCHEDULE].unit).toEqual(ScheduleUnit.MINUTES); + } + ); + + it.each(ALLOWED_SCHEDULES_IN_MINUTES)( + 'handles migrating schedule with valid schedules - http', + (validSchedule) => { + const testMonitorWithSchedule = { + ...httpUptimeUI, + attributes: { + ...httpUptimeUI.attributes, + [ConfigKey.SCHEDULE]: { + unit: ScheduleUnit.MINUTES, + number: validSchedule, + }, + }, + }; + expect( + ALLOWED_SCHEDULES_IN_MINUTES.includes( + testMonitorWithSchedule.attributes[ConfigKey.SCHEDULE].number + ) + ).toBe(true); + const actual = migration880(encryptedSavedObjectsSetup)(testMonitorWithSchedule, context); + expect(actual.attributes[ConfigKey.SCHEDULE].number).toEqual(validSchedule); + expect( + ALLOWED_SCHEDULES_IN_MINUTES.includes(actual.attributes[ConfigKey.SCHEDULE].number) + ).toBe(true); + expect(actual.attributes[ConfigKey.SCHEDULE].unit).toEqual(ScheduleUnit.MINUTES); + } + ); + + it.each(ALLOWED_SCHEDULES_IN_MINUTES)( + 'handles migrating schedule with valid schedules - tcp', + (validSchedule) => { + const testMonitorWithSchedule = { + ...tcpUptimeUI, + attributes: { + ...tcpUptimeUI.attributes, + [ConfigKey.SCHEDULE]: { + unit: ScheduleUnit.MINUTES, + number: validSchedule, + }, + }, + }; + expect( + ALLOWED_SCHEDULES_IN_MINUTES.includes( + testMonitorWithSchedule.attributes[ConfigKey.SCHEDULE].number + ) + ).toBe(true); + const actual = migration880(encryptedSavedObjectsSetup)(testMonitorWithSchedule, context); + expect(actual.attributes[ConfigKey.SCHEDULE].number).toEqual(validSchedule); + expect( + ALLOWED_SCHEDULES_IN_MINUTES.includes(actual.attributes[ConfigKey.SCHEDULE].number) + ).toBe(true); + expect(actual.attributes[ConfigKey.SCHEDULE].unit).toEqual(ScheduleUnit.MINUTES); + } + ); + + it.each(ALLOWED_SCHEDULES_IN_MINUTES)( + 'handles migrating schedule with valid schedules - icmp', + (validSchedule) => { + const testMonitorWithSchedule = { + ...icmpUptimeUI, + attributes: { + ...icmpUptimeUI.attributes, + [ConfigKey.SCHEDULE]: { + unit: ScheduleUnit.MINUTES, + number: validSchedule, + }, + }, + }; + expect( + ALLOWED_SCHEDULES_IN_MINUTES.includes( + testMonitorWithSchedule.attributes[ConfigKey.SCHEDULE].number + ) + ).toBe(true); + const actual = migration880(encryptedSavedObjectsSetup)(testMonitorWithSchedule, context); + expect(actual.attributes[ConfigKey.SCHEDULE].number).toEqual(validSchedule); + expect( + ALLOWED_SCHEDULES_IN_MINUTES.includes(actual.attributes[ConfigKey.SCHEDULE].number) + ).toBe(true); + expect(actual.attributes[ConfigKey.SCHEDULE].unit).toEqual(ScheduleUnit.MINUTES); + } + ); + + it.each(ALLOWED_SCHEDULES_IN_MINUTES)( + 'handles migrating schedule with valid schedules - project', + (validSchedule) => { + const testMonitorWithSchedule = { + ...browserProject, + attributes: { + ...browserProject.attributes, + [ConfigKey.SCHEDULE]: { + unit: ScheduleUnit.MINUTES, + number: validSchedule, + }, + }, + }; + expect( + ALLOWED_SCHEDULES_IN_MINUTES.includes( + testMonitorWithSchedule.attributes[ConfigKey.SCHEDULE].number + ) + ).toBe(true); + const actual = migration880(encryptedSavedObjectsSetup)(testMonitorWithSchedule, context); + expect(actual.attributes[ConfigKey.SCHEDULE].number).toEqual(validSchedule); + expect( + ALLOWED_SCHEDULES_IN_MINUTES.includes(actual.attributes[ConfigKey.SCHEDULE].number) + ).toBe(true); + expect(actual.attributes[ConfigKey.SCHEDULE].unit).toEqual(ScheduleUnit.MINUTES); + } + ); + + // handles invalid values stored in saved object + it.each([null, undefined, {}, []])( + 'handles migrating schedule with invalid values - browser', + (invalidSchedule) => { + const testMonitorWithSchedule = { + ...browserUptimeUI, + attributes: { + ...browserUptimeUI.attributes, + [ConfigKey.SCHEDULE]: { + unit: ScheduleUnit.MINUTES, + number: invalidSchedule, + }, + }, + }; + // @ts-ignore specificially testing monitors with invalid values for full coverage + const actual = migration880(encryptedSavedObjectsSetup)(testMonitorWithSchedule, context); + expect(actual.attributes[ConfigKey.SCHEDULE].number).toEqual('1'); + expect( + ALLOWED_SCHEDULES_IN_MINUTES.includes(actual.attributes[ConfigKey.SCHEDULE].number) + ).toBe(true); + expect(actual.attributes[ConfigKey.SCHEDULE].unit).toEqual(ScheduleUnit.MINUTES); + } + ); + + // handles invalid values stored in saved object + it.each([ + [5, '5'], + [4, '3'], + [2.5, '3'], + ])('handles migrating schedule numeric values - browser', (invalidSchedule, migrated) => { + const testMonitorWithSchedule = { + ...browserUptimeUI, + attributes: { + ...browserUptimeUI.attributes, + [ConfigKey.SCHEDULE]: { + unit: ScheduleUnit.MINUTES, + number: invalidSchedule, + }, + }, + }; + // @ts-ignore specificially testing monitors with invalid values for full coverage + const actual = migration880(encryptedSavedObjectsSetup)(testMonitorWithSchedule, context); + expect(actual.attributes[ConfigKey.SCHEDULE].number).toEqual(migrated); + expect( + ALLOWED_SCHEDULES_IN_MINUTES.includes(actual.attributes[ConfigKey.SCHEDULE].number) + ).toBe(true); + expect(actual.attributes[ConfigKey.SCHEDULE].unit).toEqual(ScheduleUnit.MINUTES); + }); + }); +}); diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/8.8.0.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/8.8.0.ts new file mode 100644 index 0000000000000..bd15dae2d3c2c --- /dev/null +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/8.8.0.ts @@ -0,0 +1,115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { omit } from 'lodash'; +import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server'; +import { SavedObjectUnsanitizedDoc } from '@kbn/core/server'; +import { + ConfigKey, + SyntheticsMonitorWithSecrets, + MonitorFields, + BrowserFields, + ScheduleUnit, +} from '../../../../../../common/runtime_types'; +import { ALLOWED_SCHEDULES_IN_MINUTES } from '../../../../../../common/constants/monitor_defaults'; +import { + LEGACY_SYNTHETICS_MONITOR_ENCRYPTED_TYPE, + SYNTHETICS_MONITOR_ENCRYPTED_TYPE, +} from '../../synthetics_monitor'; +import { validateMonitor } from '../../../../../routes/monitor_cruds/monitor_validation'; +import { + normalizeMonitorSecretAttributes, + formatSecrets, +} from '../../../../../synthetics_service/utils/secrets'; + +export const migration880 = (encryptedSavedObjects: EncryptedSavedObjectsPluginSetup) => { + return encryptedSavedObjects.createMigration< + SyntheticsMonitorWithSecrets, + SyntheticsMonitorWithSecrets + >({ + isMigrationNeededPredicate: function shouldBeMigrated( + doc + ): doc is SavedObjectUnsanitizedDoc { + return true; + }, + migration: ( + doc: SavedObjectUnsanitizedDoc, + logger + ): SavedObjectUnsanitizedDoc => { + let migrated = doc; + migrated = { + ...migrated, + attributes: { + ...migrated.attributes, + [ConfigKey.SCHEDULE]: { + number: getNearestSupportedSchedule(migrated.attributes[ConfigKey.SCHEDULE].number), + unit: ScheduleUnit.MINUTES, + }, + // when any action to change a project monitor configuration is taken + // outside of the synthetics agent cli, we should set the config hash back + // to an empty string so that the project monitors configuration + // will be updated on next push + [ConfigKey.CONFIG_HASH]: '', + }, + }; + if (migrated.attributes.type === 'browser') { + try { + const normalizedMonitorAttributes = normalizeMonitorSecretAttributes(migrated.attributes); + migrated = { + ...migrated, + attributes: omitZipUrlFields(normalizedMonitorAttributes as BrowserFields), + }; + } catch (e) { + logger.log.warn( + `Failed to remove ZIP URL fields from legacy Synthetics monitor: ${e.message}` + ); + return migrated; + } + } + return migrated; + }, + inputType: LEGACY_SYNTHETICS_MONITOR_ENCRYPTED_TYPE, + migratedType: SYNTHETICS_MONITOR_ENCRYPTED_TYPE, + }); +}; + +const getNearestSupportedSchedule = (currentSchedule: string): string => { + try { + const closest = ALLOWED_SCHEDULES_IN_MINUTES.reduce(function (prev, curr) { + const supportedSchedule = parseFloat(curr); + const currSchedule = parseFloat(currentSchedule); + const prevSupportedSchedule = parseFloat(prev); + return Math.abs(supportedSchedule - currSchedule) < + Math.abs(prevSupportedSchedule - currSchedule) + ? curr + : prev; + }); + + return closest; + } catch { + return ALLOWED_SCHEDULES_IN_MINUTES[0]; + } +}; + +const omitZipUrlFields = (fields: BrowserFields) => { + const metadata = fields[ConfigKey.METADATA]; + const updatedMetadata = omit(metadata || {}, 'is_zip_url_tls_enabled'); + // will return only fields that match the current type defs, which omit + // zip url fields + + const validationResult = validateMonitor({ + ...fields, + [ConfigKey.METADATA]: updatedMetadata, + } as MonitorFields); + + if (!validationResult.valid || !validationResult.decodedMonitor) { + throw new Error( + `Monitor is not valid: ${validationResult.reason}. ${validationResult.details}` + ); + } + + return formatSecrets(validationResult.decodedMonitor); +}; diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/index.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/index.ts index bb26f51a603e6..b200e7b09b389 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/index.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/index.ts @@ -6,7 +6,9 @@ */ import { migration860 } from './8.6.0'; +import { migration880 } from './8.8.0'; export const monitorMigrations = { '8.6.0': migration860, + '8.8.0': migration880, }; diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/test_fixtures/8.7.0.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/test_fixtures/8.7.0.ts new file mode 100644 index 0000000000000..75bc3d0452ba7 --- /dev/null +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/migrations/monitors/test_fixtures/8.7.0.ts @@ -0,0 +1,659 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { SavedObjectUnsanitizedDoc } from '@kbn/core/server'; +import { SyntheticsMonitorWithSecrets } from '../../../../../../../common/runtime_types'; + +export const browserUI = { + type: 'synthetics-monitor', + id: '311cf324-2fc9-4453-9ba5-5e745fd81722', + attributes: { + type: 'browser', + form_monitor_type: 'multistep', + enabled: true, + alert: { status: { enabled: true } }, + schedule: { unit: 'm', number: '10' }, + 'service.name': '', + config_id: '311cf324-2fc9-4453-9ba5-5e745fd81722', + tags: [], + timeout: null, + name: 'https://elastic.co', + locations: [ + { + id: 'us_central', + label: 'North America - US Central', + geo: { lat: 41.25, lon: -95.86 }, + isServiceManaged: true, + }, + ], + namespace: 'default', + origin: 'ui', + journey_id: '', + hash: '', + id: '311cf324-2fc9-4453-9ba5-5e745fd81722', + project_id: '', + playwright_options: '', + __ui: { + script_source: { is_generated_script: false, file_name: '' }, + is_zip_url_tls_enabled: false, + }, + 'url.port': null, + 'source.zip_url.url': '', + 'source.zip_url.folder': '', + 'source.zip_url.proxy_url': '', + playwright_text_assertion: '', + urls: 'https://elastic.co', + screenshots: 'on', + 'filter_journeys.match': '', + 'filter_journeys.tags': [], + ignore_https_errors: false, + 'throttling.is_enabled': true, + 'throttling.download_speed': '5', + 'throttling.upload_speed': '3', + 'throttling.latency': '20', + 'throttling.config': '5d/3u/20l', + 'ssl.certificate_authorities': '', + 'ssl.certificate': '', + 'ssl.verification_mode': 'full', + 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], + revision: 1, + secrets: + '{"params":"","source.inline.script":"step(\'Go to https://elastic.co\', async () => {\\n await page.goto(\'https://elastic.co\');\\n});","source.project.content":"","source.zip_url.username":"","source.zip_url.password":"","synthetics_args":[],"ssl.key":"","ssl.key_passphrase":""}', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-03-31T20:31:24.177Z', + created_at: '2023-03-31T20:31:24.177Z', + typeMigrationVersion: '8.6.0', +} as SavedObjectUnsanitizedDoc; +export const browserSinglePageUI = { + type: 'synthetics-monitor', + id: '7a72e681-6033-444e-b402-bddbe4a9fc4e', + attributes: { + type: 'browser', + form_monitor_type: 'single', + enabled: true, + alert: { status: { enabled: true } }, + schedule: { unit: 'm', number: '10' }, + 'service.name': '', + config_id: '7a72e681-6033-444e-b402-bddbe4a9fc4e', + tags: [], + timeout: null, + name: 'https://google.com', + locations: [{ label: 'North America - US Central', id: 'us_central', isServiceManaged: true }], + namespace: 'default', + origin: 'ui', + journey_id: '', + hash: '', + id: '7a72e681-6033-444e-b402-bddbe4a9fc4e', + project_id: '', + playwright_options: '', + __ui: { + script_source: { is_generated_script: false, file_name: '' }, + is_zip_url_tls_enabled: false, + }, + 'url.port': null, + 'source.zip_url.url': '', + 'source.zip_url.folder': '', + 'source.zip_url.proxy_url': '', + playwright_text_assertion: 'Google', + urls: 'https://google.com', + screenshots: 'on', + 'filter_journeys.match': '', + 'filter_journeys.tags': [], + ignore_https_errors: false, + 'throttling.is_enabled': true, + 'throttling.download_speed': '5', + 'throttling.upload_speed': '3', + 'throttling.latency': '20', + 'throttling.config': '5d/3u/20l', + 'ssl.certificate_authorities': '', + 'ssl.certificate': '', + 'ssl.verification_mode': 'full', + 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], + revision: 1, + secrets: + '{"params":"","source.inline.script":"step(\'Go to https://google.com\', async () => {\\n await page.goto(\'https://google.com\');\\n expect(await page.isVisible(\'text=Google\')).toBeTruthy();\\n });","source.project.content":"","source.zip_url.username":"","source.zip_url.password":"","synthetics_args":[],"ssl.key":"","ssl.key_passphrase":""}', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-03-31T20:32:01.498Z', + created_at: '2023-03-31T20:32:01.498Z', + typeMigrationVersion: '8.6.0', +} as SavedObjectUnsanitizedDoc; +export const httpUI = { + type: 'synthetics-monitor', + id: '8f4ad634-205b-440b-80c6-27aa6ef57bba', + attributes: { + type: 'http', + form_monitor_type: 'http', + enabled: true, + alert: { status: { enabled: true } }, + schedule: { number: '3', unit: 'm' }, + 'service.name': '', + config_id: '8f4ad634-205b-440b-80c6-27aa6ef57bba', + tags: [], + timeout: '16', + name: 'https://github.com', + locations: [{ label: 'North America - US Central', id: 'us_central', isServiceManaged: true }], + namespace: 'default', + origin: 'ui', + journey_id: '', + hash: '', + id: '8f4ad634-205b-440b-80c6-27aa6ef57bba', + __ui: { is_tls_enabled: false }, + urls: 'https://github.com', + max_redirects: '0', + 'url.port': null, + proxy_url: '', + 'response.include_body': 'on_error', + 'response.include_headers': true, + 'check.response.status': [], + 'check.request.method': 'GET', + 'ssl.certificate_authorities': '', + 'ssl.certificate': '', + 'ssl.verification_mode': 'full', + 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], + revision: 1, + secrets: + '{"password":"","check.request.body":{"type":"text","value":""},"check.request.headers":{},"check.response.body.negative":[],"check.response.body.positive":[],"check.response.headers":{},"ssl.key":"","ssl.key_passphrase":"","username":""}', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-03-31T20:32:14.362Z', + created_at: '2023-03-31T20:32:14.362Z', + typeMigrationVersion: '8.6.0', +} as SavedObjectUnsanitizedDoc; +export const tcpUI = { + type: 'synthetics-monitor', + id: 'b56a8fab-9a69-4435-b368-cc4fe6cdc6b0', + attributes: { + type: 'tcp', + form_monitor_type: 'tcp', + enabled: true, + alert: { status: { enabled: true } }, + schedule: { number: '3', unit: 'm' }, + 'service.name': '', + config_id: 'b56a8fab-9a69-4435-b368-cc4fe6cdc6b0', + tags: [], + timeout: '16', + name: 'smtp.gmail.com:587', + locations: [{ label: 'North America - US Central', id: 'us_central', isServiceManaged: true }], + namespace: 'default', + origin: 'ui', + journey_id: '', + hash: '', + id: 'b56a8fab-9a69-4435-b368-cc4fe6cdc6b0', + __ui: { is_tls_enabled: false }, + hosts: 'smtp.gmail.com:587', + urls: '', + 'url.port': null, + proxy_url: '', + proxy_use_local_resolver: false, + 'ssl.certificate_authorities': '', + 'ssl.certificate': '', + 'ssl.verification_mode': 'full', + 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], + revision: 1, + secrets: '{"check.send":"","check.receive":"","ssl.key":"","ssl.key_passphrase":""}', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-03-31T20:32:27.678Z', + created_at: '2023-03-31T20:32:27.678Z', + typeMigrationVersion: '8.6.0', +} as SavedObjectUnsanitizedDoc; +const icmpUI = { + type: 'synthetics-monitor', + id: '1b625301-fe0b-46c0-9980-21347c58a6f8', + attributes: { + type: 'icmp', + form_monitor_type: 'icmp', + enabled: true, + alert: { status: { enabled: true } }, + schedule: { number: '3', unit: 'm' }, + 'service.name': '', + config_id: '1b625301-fe0b-46c0-9980-21347c58a6f8', + tags: [], + timeout: '16', + name: '1.1.1.1', + locations: [{ label: 'North America - US Central', id: 'us_central', isServiceManaged: true }], + namespace: 'default', + origin: 'ui', + journey_id: '', + hash: '', + id: '1b625301-fe0b-46c0-9980-21347c58a6f8', + hosts: '1.1.1.1', + wait: '1', + revision: 1, + secrets: '{}', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-03-31T20:32:39.147Z', + created_at: '2023-03-31T20:32:39.147Z', + typeMigrationVersion: '8.6.0', +} as SavedObjectUnsanitizedDoc; +export const browserUptimeUI = { + type: 'synthetics-monitor', + id: '9bf12063-271f-47b1-9121-db1d14a71bb3', + attributes: { + type: 'browser', + form_monitor_type: 'multistep', + enabled: true, + alert: { status: { enabled: true } }, + schedule: { number: '240', unit: 'm' }, + 'service.name': '', + config_id: '9bf12063-271f-47b1-9121-db1d14a71bb3', + tags: [], + timeout: null, + name: 'A browser monitor with an invalid schedule 600', + locations: [ + { + id: 'us_central', + label: 'North America - US Central', + geo: { lat: 41.25, lon: -95.86 }, + isServiceManaged: true, + }, + ], + namespace: 'default', + origin: 'ui', + journey_id: '', + hash: '', + id: '9bf12063-271f-47b1-9121-db1d14a71bb3', + project_id: '', + playwright_options: '', + __ui: { + script_source: { is_generated_script: false, file_name: '' }, + is_zip_url_tls_enabled: false, + is_tls_enabled: false, + }, + 'url.port': null, + 'source.zip_url.url': '', + 'source.zip_url.folder': '', + 'source.zip_url.proxy_url': '', + playwright_text_assertion: '', + urls: '', + screenshots: 'on', + 'filter_journeys.match': '', + 'filter_journeys.tags': [], + ignore_https_errors: false, + 'throttling.is_enabled': true, + 'throttling.download_speed': '5', + 'throttling.upload_speed': '3', + 'throttling.latency': '20', + 'throttling.config': '5d/3u/20l', + 'ssl.certificate_authorities': '', + 'ssl.certificate': '', + 'ssl.verification_mode': 'full', + 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], + revision: 1, + secrets: + '{"params":"","source.inline.script":"lkjelre","source.project.content":"","source.zip_url.username":"","source.zip_url.password":"","synthetics_args":[],"ssl.key":"","ssl.key_passphrase":""}', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-03-31T20:35:34.916Z', + created_at: '2023-03-31T20:35:34.916Z', + typeMigrationVersion: '8.6.0', +} as SavedObjectUnsanitizedDoc; +export const tcpUptimeUI = { + type: 'synthetics-monitor', + id: '726d3f74-7760-4045-ad8d-87642403c721', + attributes: { + type: 'tcp', + form_monitor_type: 'tcp', + enabled: true, + alert: { status: { enabled: true } }, + schedule: { number: '8', unit: 'm' }, + 'service.name': '', + config_id: '726d3f74-7760-4045-ad8d-87642403c721', + tags: [], + timeout: '16', + name: 'TCP monitor with invalid schedule 8m', + locations: [ + { + id: 'us_central', + label: 'North America - US Central', + geo: { lat: 41.25, lon: -95.86 }, + isServiceManaged: true, + }, + ], + namespace: 'default', + origin: 'ui', + journey_id: '', + hash: '', + id: '726d3f74-7760-4045-ad8d-87642403c721', + __ui: { is_tls_enabled: false, is_zip_url_tls_enabled: false }, + hosts: 'localhost:5601', + urls: '', + 'url.port': null, + proxy_url: '', + proxy_use_local_resolver: false, + 'ssl.certificate_authorities': '', + 'ssl.certificate': '', + 'ssl.verification_mode': 'full', + 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], + revision: 1, + secrets: '{"check.send":"","check.receive":"","ssl.key":"","ssl.key_passphrase":""}', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-03-31T20:38:29.582Z', + created_at: '2023-03-31T20:38:29.582Z', + typeMigrationVersion: '8.6.0', +} as SavedObjectUnsanitizedDoc; +export const httpUptimeUI = { + type: 'synthetics-monitor', + id: '35b2d765-4a62-4511-91c8-d5d52fdf4639', + attributes: { + type: 'http', + form_monitor_type: 'http', + enabled: true, + alert: { status: { enabled: true } }, + schedule: { number: '4', unit: 'm' }, + 'service.name': '', + config_id: '35b2d765-4a62-4511-91c8-d5d52fdf4639', + tags: [], + timeout: '16', + name: 'HTTP monitor with invalid schedule 4m', + locations: [ + { + id: 'us_central', + label: 'North America - US Central', + geo: { lat: 41.25, lon: -95.86 }, + isServiceManaged: true, + }, + ], + namespace: 'default', + origin: 'ui', + journey_id: '', + hash: '', + id: '35b2d765-4a62-4511-91c8-d5d52fdf4639', + __ui: { is_tls_enabled: false, is_zip_url_tls_enabled: false }, + urls: 'https://google.com', + max_redirects: '0', + 'url.port': null, + proxy_url: '', + 'response.include_body': 'on_error', + 'response.include_headers': true, + 'check.response.status': [], + 'check.request.method': 'GET', + 'ssl.certificate_authorities': '', + 'ssl.certificate': '', + 'ssl.verification_mode': 'full', + 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], + revision: 1, + secrets: + '{"password":"","check.request.body":{"type":"text","value":""},"check.request.headers":{},"check.response.body.negative":[],"check.response.body.positive":[],"check.response.headers":{},"ssl.key":"","ssl.key_passphrase":"","username":""}', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-03-31T20:37:24.093Z', + created_at: '2023-03-31T20:37:24.093Z', + typeMigrationVersion: '8.6.0', +} as SavedObjectUnsanitizedDoc; +export const icmpUptimeUI = { + type: 'synthetics-monitor', + id: '28b14c99-4a39-475d-9545-21b35b35751d', + attributes: { + type: 'icmp', + form_monitor_type: 'icmp', + enabled: true, + alert: { status: { enabled: true } }, + schedule: { number: '11', unit: 'm' }, + 'service.name': '', + config_id: '28b14c99-4a39-475d-9545-21b35b35751d', + tags: [], + timeout: '16', + name: 'ICMP monitor with invalid schedule 11m', + locations: [ + { + geo: { lon: -95.86, lat: 41.25 }, + isServiceManaged: true, + id: 'us_central', + label: 'North America - US Central', + }, + ], + namespace: 'default', + origin: 'ui', + journey_id: '', + hash: '', + id: '28b14c99-4a39-475d-9545-21b35b35751d', + hosts: '1.1.1.1', + wait: '1', + revision: 2, + secrets: '{}', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-03-31T20:40:28.889Z', + created_at: '2023-03-31T20:39:13.783Z', + typeMigrationVersion: '8.6.0', +} as SavedObjectUnsanitizedDoc; +export const browserProject = { + type: 'synthetics-monitor', + id: 'ea123f46-eb02-4a8a-b3ce-53e645ce4aef', + attributes: { + type: 'browser', + form_monitor_type: 'multistep', + enabled: true, + alert: { status: { enabled: true } }, + schedule: { number: '10', unit: 'm' }, + 'service.name': '', + config_id: 'ea123f46-eb02-4a8a-b3ce-53e645ce4aef', + tags: [], + timeout: null, + name: 'addition and completion of single task', + locations: [ + { + id: 'us_central', + label: 'North America - US Central', + geo: { lat: 41.25, lon: -95.86 }, + isServiceManaged: true, + }, + ], + namespace: 'default', + origin: 'project', + journey_id: 'addition and completion of single task', + hash: '7a7cyPraVarTWfDHyqSgXBktTAcuwwWtcB+IGdNZF14=', + id: 'addition and completion of single task-test2-default', + project_id: 'test2', + playwright_options: '{"ignoreHTTPSErrors":true,"headless":true}', + __ui: { + script_source: { is_generated_script: false, file_name: '' }, + is_zip_url_tls_enabled: false, + }, + 'url.port': null, + 'source.zip_url.url': '', + 'source.zip_url.folder': '', + 'source.zip_url.proxy_url': '', + playwright_text_assertion: '', + urls: '', + screenshots: 'on', + 'filter_journeys.match': 'addition and completion of single task', + 'filter_journeys.tags': [], + ignore_https_errors: false, + 'throttling.is_enabled': true, + 'throttling.download_speed': '5', + 'throttling.upload_speed': '3', + 'throttling.latency': '20', + 'throttling.config': '5d/3u/20l', + 'ssl.certificate_authorities': '', + 'ssl.certificate': '', + 'ssl.verification_mode': 'full', + 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], + original_space: 'default', + custom_heartbeat_id: 'addition and completion of single task-test2-default', + revision: 1, + secrets: + '{"params":"{\\"url\\":\\"https://elastic.github.io/synthetics-demo/\\"}","source.inline.script":"","source.project.content":"UEsDBBQACAAIAAAAIQAAAAAAAAAAAAAAAAAkAAAAam91cm5leXMvYWR2YW5jZWQtZXhhbXBsZS5qb3VybmV5LnRzpVVrb9pKEP2eX2H5XqlESsCQ0DatUt3wMDE1tDbYPKrqZrE3YDC2410epsp/v7PrB4aQKNL9wrI7M2fOnJldl0rC3F+FHo5ICdlr5FnYvsRbtAxcXEwsRUrO1igUnGXgh/RfEnl0hqljkYpwK4T4aeWEuCD+g11E4LS0t4vnX8/OSm9kuJxhN8AheTXDOxKwONdH9l0Q9CgOIKIQoCm+EFahey7cfhP+nAlCQbp4CV4k4H9eEF208qyZgIJAvBAQ2C2hkEUKAtoghwoMszj1qV9guF/B8gy/z3F+ZNt9RBaH+SmcvIvAA4Rzb+HvP2x5fjhFw/I9QgXHC1YUcuRIub6FqB8WRG4renhzSX3bFznJlH5so1GAC5zXS1sQYkIKYtOjOBSPC2SApIddbEEmSC+u3CI7u3QdIOU6fCPGvo+Ox9VgLOMqTugRYgozcaqMBxAhn+1Z+PZNoHhLb8VEHvFhT8yaYWsh+2FO/teTvtkEjiQ8Qnm8FR+SZB9AHoFVebIpSRlpyfmkRwramNDQj/4/zwToiOUbI+OiCXazkXmNaubtZK48cD9e2wDR2S27vwT2X764zq/yb/EgOOFWW1Hqe3scZw8y4abDKMt1oIcrUBJDzAFGkdsKB+MKcDN/jcPD0xxITvk3X5/8+3ZS8krqAY8E3FGHOlAV8mzgzeL51n8UiONNXcylhAek8EeIpQ1QiJYEmKQNiaulUF8fxpndoobvfYDxhxtNkMsuohBBQgFHmIisity7VsiDFtM3KPfwpA1N4Ln5+Hac8jkazJcuTMp3yeNNuTghXvprtlmuXOoEiTLkvdIwX9Dml8jfkDKExf8q2b8r8fe7xMnginCrm8iaFQo0/66/1C67tG/KwiBh9F9pQMzxWkyUK5X+EgjIZOEOfGBAFkNXb21E0RfYwswiNkWlOfG9rxNE8MfrC7zxp0q9XRkP2zs0uFkpc3/aiTbflfqdY1Vuytay61qR8lGRiYOWbF918bCzHg30ymhQnY8HGh0PZ7PJsEbGverT5MqMJkuzqnr6TnE2U2UZrO1hezUeuju1Ii/spbwaVcyFOjCvR4PyZtIyKGqZxGqZkRVVJStq2+od50RHw9oGDaoez19XPmmGXNN623aybrWFXLMl5dOPHa1pknytOdsrdjYxOsy32zUU0gPffm87h/1cbXZrmtEhfTgb9LY6nHnaHGKNrqTXN9csthclvrJb08wFw2l0JYWYMY4Ke1VtshhCdEmu6Sw32NSmWdOu2vUkB4/rNZjN/Kk2AcuQyAj8R9G2wWxqs8zwiQFnyOC1NeyFRn402xx7sOeo57lMFp2US2xjOIDdkzj3+Kxl13O1Pen1rcn0OPJlOrZ/wH/OWza5rR9jd2P/Gzj7nOouQ1070Jjz53HSlNnkpCfdw7zmMq3bBF8z2v4E289TdXcMje/7UZo31teAvEZvew9n92qjBtjNDeTnODx/ZSaz1ZA+p32mcf8V0t/jjVUZejJw63HMVlYbszrvY6vMVgqYAYsDPrHufa7bCnQbcs4s3uyk2ux1M5pEZ/MBcRrb81noKsymGxmnkGNLb3EKcpxctgbAqQ9rP997/ErvmXZGph3TnPvL9t4/mdtyvscxxr3J8pX39yCZlbSXToprg91Ke8JjeZ3RTSOdnWS+k7vGc3EeuJnxiHuZs42dOF9eT+NoxuB+t/X7WUPrbSSmpdHbbJjeHcDlukdsNmU5qy83S/F867Iqy/tZ5H0yn6C2Oe/FYRznyON2Zl1tdfm85jWLbXI9mZ8g04jlMJu8Vyr0G0lwlwd2PesT0y6eLV+LcjVC3TArMrxBSX3bHavLkt5ZH5vppL7k3YjfGNYX0+Dam/me9aBnh3Uld5P3OH7zkrtgZrPG3gLCbdZCIWNW2z7+oBY98dnfCbuptujhPCXcwLemc01sokWH/djr1Sln706kkLpzB9+V6mwyMHfsezWI2gF8f9aWpz9aV+7Kbs1cu+XO2bek7knfz/4DUEsHCBVHLjjcBQAAeA4AAFBLAQItAxQACAAIAAAAIQAVRy443AUAAHgOAAAkAAAAAAAAAAAAIACkgQAAAABqb3VybmV5cy9hZHZhbmNlZC1leGFtcGxlLmpvdXJuZXkudHNQSwUGAAAAAAEAAQBSAAAALgYAAAAA","source.zip_url.username":"","source.zip_url.password":"","synthetics_args":[],"ssl.key":"","ssl.key_passphrase":""}', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-03-31T20:43:35.214Z', + created_at: '2023-03-31T20:43:35.214Z', + typeMigrationVersion: '8.6.0', +} as SavedObjectUnsanitizedDoc; +export const httpProject = { + type: 'synthetics-monitor', + id: '316c0df8-56fc-428a-a477-7bf580f6cb4c', + attributes: { + type: 'http', + form_monitor_type: 'http', + enabled: true, + alert: { status: { enabled: true } }, + schedule: { number: '10', unit: 'm' }, + 'service.name': '', + config_id: '316c0df8-56fc-428a-a477-7bf580f6cb4c', + tags: ['org:elastics'], + timeout: '16', + name: 'facebook', + locations: [ + { + id: 'us_central', + label: 'North America - US Central', + geo: { lat: 41.25, lon: -95.86 }, + isServiceManaged: true, + }, + ], + namespace: 'default', + origin: 'project', + journey_id: 'an-id3', + hash: 'thcZtI5hzo94RiDoK1B+MEwIPIzJMINtuA042Y+yrDU=', + id: 'an-id3-test2-default', + __ui: { is_tls_enabled: false }, + urls: 'https://www.facebook.com', + max_redirects: '0', + 'url.port': null, + proxy_url: '', + 'response.include_body': 'on_error', + 'response.include_headers': true, + 'check.response.status': [], + 'check.request.method': 'GET', + 'ssl.certificate_authorities': '', + 'ssl.certificate': '', + 'ssl.verification_mode': 'full', + 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], + project_id: 'test2', + original_space: 'default', + custom_heartbeat_id: 'an-id3-test2-default', + revision: 1, + secrets: + '{"password":"","check.request.body":{"type":"text","value":""},"check.request.headers":{"Content-Type":"text/plain"},"check.response.body.negative":[],"check.response.body.positive":[],"check.response.headers":{},"ssl.key":"","ssl.key_passphrase":"","username":""}', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-03-31T20:43:35.214Z', + created_at: '2023-03-31T20:43:35.214Z', + typeMigrationVersion: '8.6.0', +} as SavedObjectUnsanitizedDoc; +export const icmpProject = { + type: 'synthetics-monitor', + id: 'e21a30b5-6d40-4458-8cff-9003d7b83eb6', + attributes: { + type: 'icmp', + form_monitor_type: 'icmp', + enabled: true, + alert: { status: { enabled: true } }, + schedule: { number: '10', unit: 'm' }, + 'service.name': '', + config_id: 'e21a30b5-6d40-4458-8cff-9003d7b83eb6', + tags: ['service:dns', 'org:cloudflare'], + timeout: '16', + name: 'Cloudflare DNS', + locations: [ + { + id: 'us_central', + label: 'North America - US Central', + geo: { lat: 41.25, lon: -95.86 }, + isServiceManaged: true, + }, + ], + namespace: 'default', + origin: 'project', + journey_id: 'stuff', + hash: 'fZfJJOKGdjznBxHZLgLrWbkvUI/AH4SzFqweV/NnpIw=', + id: 'stuff-test2-default', + hosts: '${random_host}', + wait: '1', + project_id: 'test2', + original_space: 'default', + custom_heartbeat_id: 'stuff-test2-default', + revision: 1, + secrets: '{}', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-03-31T20:43:35.214Z', + created_at: '2023-03-31T20:43:35.214Z', + typeMigrationVersion: '8.6.0', +} as SavedObjectUnsanitizedDoc; +export const tcpProject = { + type: 'synthetics-monitor', + id: '9f5d6206-9a1d-47fb-bd67-c7895b07f716', + attributes: { + type: 'tcp', + form_monitor_type: 'tcp', + enabled: true, + alert: { status: { enabled: false } }, + schedule: { number: '30', unit: 'm' }, + 'service.name': '', + config_id: '9f5d6206-9a1d-47fb-bd67-c7895b07f716', + tags: ['service:smtp', 'org:google'], + timeout: '16', + name: 'GMail SMTP', + locations: [ + { + geo: { lon: -95.86, lat: 41.25 }, + isServiceManaged: true, + id: 'us_central', + label: 'North America - US Central', + }, + ], + namespace: 'default', + origin: 'project', + journey_id: 'gmail-smtp', + hash: 'BoPnjeryNLnktKz+PeeHwHKzEnZaxHNJmAUjlOVKfRY=', + id: 'gmail-smtp-test2-default', + __ui: { is_tls_enabled: false }, + hosts: '${random_host}', + urls: '', + 'url.port': null, + proxy_url: '', + proxy_use_local_resolver: false, + 'ssl.certificate_authorities': '', + 'ssl.certificate': '', + 'ssl.verification_mode': 'full', + 'ssl.supported_protocols': ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], + project_id: 'test2', + original_space: 'default', + custom_heartbeat_id: 'gmail-smtp-test2-default', + revision: 3, + secrets: '{"check.send":"","check.receive":"","ssl.key":"","ssl.key_passphrase":""}', + }, + references: [], + coreMigrationVersion: '8.8.0', + updated_at: '2023-03-31T20:47:15.781Z', + created_at: '2023-03-31T20:43:35.214Z', + typeMigrationVersion: '8.6.0', +} as SavedObjectUnsanitizedDoc; + +export const testMonitors = [ + browserUI, + browserSinglePageUI, + httpUI, + tcpUI, + icmpUI, + browserUptimeUI, + httpUptimeUI, + tcpUptimeUI, + icmpUptimeUI, + browserProject, + httpProject, + tcpProject, + icmpProject, +]; diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/synthetics_monitor.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/synthetics_monitor.ts index 0a766e4a5833d..3d5ddecf188a5 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/synthetics_monitor.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/saved_objects/synthetics_monitor.ts @@ -7,11 +7,31 @@ import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server'; import { SavedObjectsType } from '@kbn/core/server'; import { i18n } from '@kbn/i18n'; -import { secretKeys } from '../../../../common/constants/monitor_management'; +import { + secretKeys, + ConfigKey, + LegacyConfigKey, +} from '../../../../common/constants/monitor_management'; import { monitorMigrations } from './migrations/monitors'; export const syntheticsMonitorType = 'synthetics-monitor'; +const legacyConfigKeys = Object.values(LegacyConfigKey); + +export const LEGACY_SYNTHETICS_MONITOR_ENCRYPTED_TYPE = { + type: syntheticsMonitorType, + attributesToEncrypt: new Set([ + 'secrets', + /* adding secretKeys to the list of attributes to encrypt ensures + * that secrets are never stored on the resulting saved object, + * even in the presence of developer error. + * + * In practice, all secrets should be stored as a single JSON + * payload on the `secrets` key. This ensures performant decryption. */ + ...secretKeys, + ]), +}; + export const SYNTHETICS_MONITOR_ENCRYPTED_TYPE = { type: syntheticsMonitorType, attributesToEncrypt: new Set([ @@ -24,6 +44,11 @@ export const SYNTHETICS_MONITOR_ENCRYPTED_TYPE = { * payload on the `secrets` key. This ensures performant decryption. */ ...secretKeys, ]), + attributesToExcludeFromAAD: new Set([ + ConfigKey.ALERT_CONFIG, + ConfigKey.METADATA, + ...legacyConfigKeys, + ]), }; export const getSyntheticsMonitorSavedObjectType = ( @@ -35,6 +60,7 @@ export const getSyntheticsMonitorSavedObjectType = ( namespaceType: 'single', migrations: { '8.6.0': monitorMigrations['8.6.0'](encryptedSavedObjects), + '8.8.0': monitorMigrations['8.8.0'](encryptedSavedObjects), }, mappings: { dynamic: false, diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.test.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.test.ts index b927e1a37e21a..98188e88dc7ff 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.test.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.test.ts @@ -29,7 +29,6 @@ import { TLSFields, TLSVersion, VerificationMode, - ZipUrlTLSFields, } from '../../../common/runtime_types'; import { validateMonitor } from './monitor_validation'; @@ -46,7 +45,6 @@ describe('validateMonitor', () => { let testHTTPSimpleFields: HTTPSimpleFields; let testHTTPAdvancedFields: HTTPAdvancedFields; let testHTTPFields: HTTPFields; - let testZipUrlTLSFields: ZipUrlTLSFields; let testBrowserSimpleFields: BrowserSimpleFields; let testBrowserAdvancedFields: BrowserAdvancedFields; let testBrowserFields: BrowserFields; @@ -81,7 +79,6 @@ describe('validateMonitor', () => { }; testMetaData = { is_tls_enabled: false, - is_zip_url_tls_enabled: false, script_source: { is_generated_script: false, file_name: 'test-file.name', @@ -158,17 +155,7 @@ describe('validateMonitor', () => { [ConfigKey.MONITOR_TYPE]: DataStream.HTTP, }; - testZipUrlTLSFields = { - [ConfigKey.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: 'test', - [ConfigKey.ZIP_URL_TLS_CERTIFICATE]: 'test', - [ConfigKey.ZIP_URL_TLS_KEY]: 'key', - [ConfigKey.ZIP_URL_TLS_KEY_PASSPHRASE]: 'passphrase', - [ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE]: VerificationMode.STRICT, - [ConfigKey.ZIP_URL_TLS_VERSION]: [TLSVersion.ONE_ONE, TLSVersion.ONE_TWO], - }; - testBrowserSimpleFields = { - ...testZipUrlTLSFields, ...testCommonFields, [ConfigKey.FORM_MONITOR_TYPE]: FormMonitorType.MULTISTEP, [ConfigKey.MONITOR_SOURCE_TYPE]: SourceType.PROJECT, @@ -177,11 +164,6 @@ describe('validateMonitor', () => { [ConfigKey.METADATA]: testMetaData, [ConfigKey.SOURCE_INLINE]: '', [ConfigKey.SOURCE_PROJECT_CONTENT]: '', - [ConfigKey.SOURCE_ZIP_URL]: '', - [ConfigKey.SOURCE_ZIP_FOLDER]: '', - [ConfigKey.SOURCE_ZIP_USERNAME]: 'test-username', - [ConfigKey.SOURCE_ZIP_PASSWORD]: 'password', - [ConfigKey.SOURCE_ZIP_PROXY_URL]: 'http://proxy-url.com', [ConfigKey.PARAMS]: '', [ConfigKey.URLS]: null, [ConfigKey.PORT]: null, @@ -209,7 +191,13 @@ describe('validateMonitor', () => { describe('should invalidate', () => { it(`when 'type' is null or undefined`, () => { - const testMonitor = { type: undefined } as unknown as MonitorFields; + const testMonitor = { + type: undefined, + schedule: { + unit: ScheduleUnit.MINUTES, + number: '3', + }, + } as unknown as MonitorFields; const result = validateMonitor(testMonitor); expect(result).toMatchObject({ valid: false, @@ -219,7 +207,13 @@ describe('validateMonitor', () => { }); it(`when 'type' is not an acceptable monitor type (DataStream)`, () => { - const monitor = { type: 'non-HTTP' } as unknown as MonitorFields; + const monitor = { + type: 'non-HTTP', + schedule: { + unit: ScheduleUnit.MINUTES, + number: '3', + }, + } as unknown as MonitorFields; const result = validateMonitor(monitor); expect(result).toMatchObject({ valid: false, @@ -227,6 +221,22 @@ describe('validateMonitor', () => { details: expect.stringMatching(/(?=.*invalid)(?=.*non-HTTP)(?=.*DataStream)/i), }); }); + + it(`when schedule is not valid`, () => { + const result = validateMonitor({ + ...testICMPFields, + schedule: { + number: '4', + unit: ScheduleUnit.MINUTES, + }, + } as unknown as MonitorFields); + expect(result).toMatchObject({ + valid: false, + reason: 'Monitor schedule is invalid', + details: + 'Invalid schedule 4 minutes supplied to monitor configuration. Please use a supported monitor schedule.', + }); + }); }); describe('should validate', () => { @@ -408,7 +418,6 @@ function getJsonPayload() { ' "timeout": "3m",' + ' "__ui": {' + ' "is_tls_enabled": false,' + - ' "is_zip_url_tls_enabled": false,' + ' "script_source": {' + ' "is_generated_script": false,' + ' "file_name": "test-file.name"' + diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.ts index 77cd0fa23a7ce..4f091c5e7c417 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/monitor_validation.ts @@ -23,6 +23,8 @@ import { SyntheticsMonitor, } from '../../../common/runtime_types'; +import { ALLOWED_SCHEDULES_IN_MINUTES } from '../../../common/constants/monitor_defaults'; + type MonitorCodecType = | typeof ICMPSimpleFieldsCodec | typeof TCPFieldsCodec @@ -52,6 +54,7 @@ export function validateMonitor(monitorFields: MonitorFields): ValidationResult const { [ConfigKey.MONITOR_TYPE]: monitorType } = monitorFields; const decodedType = DataStreamCodec.decode(monitorType); + if (isLeft(decodedType)) { return { valid: false, @@ -73,6 +76,17 @@ export function validateMonitor(monitorFields: MonitorFields): ValidationResult }; } + if (!ALLOWED_SCHEDULES_IN_MINUTES.includes(monitorFields[ConfigKey.SCHEDULE].number)) { + return { + valid: false, + reason: `Monitor schedule is invalid`, + details: `Invalid schedule ${ + monitorFields[ConfigKey.SCHEDULE].number + } minutes supplied to monitor configuration. Please use a supported monitor schedule.`, + payload: monitorFields, + }; + } + const ExactSyntheticsMonitorCodec = t.exact(SyntheticsMonitorCodec); const decodedMonitor = ExactSyntheticsMonitorCodec.decode(monitorFields); diff --git a/x-pack/plugins/synthetics/server/routes/telemetry/monitor_upgrade_sender.test.ts b/x-pack/plugins/synthetics/server/routes/telemetry/monitor_upgrade_sender.test.ts index 65ee31dbb08aa..6dc14565b6289 100644 --- a/x-pack/plugins/synthetics/server/routes/telemetry/monitor_upgrade_sender.test.ts +++ b/x-pack/plugins/synthetics/server/routes/telemetry/monitor_upgrade_sender.test.ts @@ -117,7 +117,6 @@ describe('monitor upgrade telemetry helpers', () => { [ConfigKey.MONITOR_SOURCE_TYPE, SourceType.PROJECT, 'project', false, false], [ConfigKey.SOURCE_INLINE, 'test', 'recorder', true, true], [ConfigKey.SOURCE_INLINE, 'test', 'inline', false, true], - [ConfigKey.SOURCE_ZIP_URL, 'test', 'zip', false, false], ])( 'handles formatting scriptType for browser monitors', (config, value, scriptType, isRecorder, isInlineScript) => { diff --git a/x-pack/plugins/synthetics/server/routes/telemetry/monitor_upgrade_sender.ts b/x-pack/plugins/synthetics/server/routes/telemetry/monitor_upgrade_sender.ts index d86792a7967c3..fcfce9d2c85a1 100644 --- a/x-pack/plugins/synthetics/server/routes/telemetry/monitor_upgrade_sender.ts +++ b/x-pack/plugins/synthetics/server/routes/telemetry/monitor_upgrade_sender.ts @@ -165,8 +165,6 @@ function getScriptType( isInlineScript: boolean ): MonitorUpdateEvent['scriptType'] | undefined { switch (true) { - case Boolean(attributes[ConfigKey.SOURCE_ZIP_URL]): - return 'zip'; case Boolean( isInlineScript && attributes[ConfigKey.METADATA]?.script_source?.is_generated_script ): diff --git a/x-pack/plugins/synthetics/server/synthetics_service/formatters/browser.ts b/x-pack/plugins/synthetics/server/synthetics_service/formatters/browser.ts index 1a5e090033ece..391556bb99649 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/formatters/browser.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/formatters/browser.ts @@ -36,13 +36,7 @@ const throttlingFormatter: Formatter = (fields) => { export const browserFormatters: BrowserFormatMap = { ...basicBrowserFormatters, [ConfigKey.METADATA]: objectFormatter, - [ConfigKey.ZIP_URL_TLS_VERSION]: arrayFormatter, [ConfigKey.SOURCE_INLINE]: null, - [ConfigKey.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: null, - [ConfigKey.ZIP_URL_TLS_CERTIFICATE]: null, - [ConfigKey.ZIP_URL_TLS_KEY]: null, - [ConfigKey.ZIP_URL_TLS_KEY_PASSPHRASE]: null, - [ConfigKey.ZIP_URL_TLS_VERIFICATION_MODE]: null, [ConfigKey.THROTTLING_CONFIG]: throttlingFormatter, [ConfigKey.JOURNEY_FILTERS_MATCH]: null, [ConfigKey.SYNTHETICS_ARGS]: arrayFormatter, diff --git a/x-pack/plugins/synthetics/server/synthetics_service/formatters/format_configs.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/formatters/format_configs.test.ts index 139c9faa91854..eb2b40e324249 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/formatters/format_configs.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/formatters/format_configs.test.ts @@ -32,7 +32,7 @@ const testHTTPConfig: Partial = { timeout: '16', name: 'Test', locations: [], - __ui: { is_tls_enabled: false, is_zip_url_tls_enabled: false }, + __ui: { is_tls_enabled: false }, urls: 'https://www.google.com', max_redirects: '0', password: '3z9SBOQWW5F0UrdqLVFqlF6z', @@ -62,14 +62,8 @@ const testBrowserConfig: Partial = { locations: [], __ui: { script_source: { is_generated_script: false, file_name: '' }, - is_zip_url_tls_enabled: false, is_tls_enabled: false, }, - 'source.zip_url.url': '', - 'source.zip_url.username': '', - 'source.zip_url.password': '', - 'source.zip_url.folder': '', - 'source.zip_url.proxy_url': '', 'source.inline.script': "step('Go to https://www.google.com/', async () => {\n await page.goto('https://www.google.com/');\n});", params: '{"a":"param"}', diff --git a/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.test.ts index 4d319c714a44b..5f1c74d5dfb05 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/private_location/synthetics_private_location.test.ts @@ -198,7 +198,7 @@ describe('SyntheticsPrivateLocation', () => { __ui: { type: 'yaml', value: - '{"script_source":{"is_generated_script":false,"file_name":""},"is_zip_url_tls_enabled":false,"is_tls_enabled":true}', + '{"script_source":{"is_generated_script":false,"file_name":""},"is_tls_enabled":true}', }, config_id: { type: 'text', @@ -253,44 +253,6 @@ describe('SyntheticsPrivateLocation', () => { value: "\"step('Go to https://www.elastic.co/', async () => {\\n await page.goto('https://www.elastic.co/');\\n});\"", }, - 'source.zip_url.folder': { - type: 'text', - value: '', - }, - 'source.zip_url.password': { - type: 'password', - value: '', - }, - 'source.zip_url.proxy_url': { - type: 'text', - value: '', - }, - 'source.zip_url.ssl.certificate': { - type: 'yaml', - }, - 'source.zip_url.ssl.certificate_authorities': { - type: 'yaml', - }, - 'source.zip_url.ssl.key': { - type: 'yaml', - }, - 'source.zip_url.ssl.key_passphrase': { - type: 'text', - }, - 'source.zip_url.ssl.supported_protocols': { - type: 'yaml', - }, - 'source.zip_url.ssl.verification_mode': { - type: 'text', - }, - 'source.zip_url.url': { - type: 'text', - value: '', - }, - 'source.zip_url.username': { - type: 'text', - value: '', - }, synthetics_args: { type: 'text', value: null, @@ -336,7 +298,6 @@ const dummyBrowserConfig: Partial & { playwright_options: '', __ui: { script_source: { is_generated_script: false, file_name: '' }, - is_zip_url_tls_enabled: false, is_tls_enabled: true, }, params: '', @@ -344,11 +305,6 @@ const dummyBrowserConfig: Partial & { 'source.inline.script': "step('Go to https://www.elastic.co/', async () => {\n await page.goto('https://www.elastic.co/');\n});", 'source.project.content': '', - 'source.zip_url.url': '', - 'source.zip_url.username': '', - 'source.zip_url.password': '', - 'source.zip_url.folder': '', - 'source.zip_url.proxy_url': '', urls: 'https://www.elastic.co/', screenshots: 'on', synthetics_args: [], diff --git a/x-pack/plugins/synthetics/server/synthetics_service/private_location/test_policy.ts b/x-pack/plugins/synthetics/server/synthetics_service/private_location/test_policy.ts index 597045ea45973..266d440e4df86 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/private_location/test_policy.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/private_location/test_policy.ts @@ -135,10 +135,6 @@ export const testMonitorPolicy = { 'service.name': { type: 'text' }, timeout: { type: 'text' }, tags: { type: 'yaml' }, - 'source.zip_url.url': { type: 'text' }, - 'source.zip_url.username': { type: 'text' }, - 'source.zip_url.folder': { type: 'text' }, - 'source.zip_url.password': { type: 'password' }, 'source.inline.script': { type: 'yaml' }, params: { type: 'yaml' }, screenshots: { type: 'text' }, @@ -147,13 +143,6 @@ export const testMonitorPolicy = { 'throttling.config': { type: 'text' }, 'filter_journeys.tags': { type: 'yaml' }, 'filter_journeys.match': { type: 'text' }, - 'source.zip_url.ssl.certificate_authorities': { type: 'yaml' }, - 'source.zip_url.ssl.certificate': { type: 'yaml' }, - 'source.zip_url.ssl.key': { type: 'yaml' }, - 'source.zip_url.ssl.key_passphrase': { type: 'text' }, - 'source.zip_url.ssl.verification_mode': { type: 'text' }, - 'source.zip_url.ssl.supported_protocols': { type: 'yaml' }, - 'source.zip_url.proxy_url': { type: 'text' }, location_name: { value: 'Fleet managed', type: 'text' }, config_id: { type: 'text' }, run_once: { value: false, type: 'bool' }, diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.test.ts index f085c73be2ba1..800b8cfa0ac5e 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.test.ts @@ -445,7 +445,6 @@ const payloadData = [ { ...DEFAULT_FIELDS[DataStream.BROWSER], __ui: { - is_zip_url_tls_enabled: false, script_source: { file_name: '', is_generated_script: false, @@ -478,12 +477,6 @@ const payloadData = [ 'source.inline.script': '', 'source.project.content': 'UEsDBBQACAAIAAAAIQAAAAAAAAAAAAAAAAAQAAAAYmFzaWMuam91cm5leS50c2WQQU7DQAxF9znFV8QiUUOmXcCCUMQl2NdMnWbKJDMaO6Ilyt0JASQkNv9Z1teTZWNAIqwP5kU4iZGOug863u7uDXsSddbIddCOl0kMX6iPnsVoOAYxryTO1ucwpoGvtUrm+hiSYsLProIoxwp8iWwVM9oUeuTP/9V5k7UhofCscNhj2yx4xN2CzabElOHXWRxsx/YNroU69QwniImFB8Vui5vJzYcKxYRIJ66WTNQL5hL7p1WD9aYi9zQOtgPFGPNqecJ1sCj+tAB6J6erpj4FDcW3qh6TL5u1Mq/8yjn7BFBLBwhGDIWc4QAAAEkBAABQSwECLQMUAAgACAAAACEARgyFnOEAAABJAQAAEAAAAAAAAAAAACAApIEAAAAAYmFzaWMuam91cm5leS50c1BLBQYAAAAAAQABAD4AAAAfAQAAAAA=', - 'source.zip_url.folder': '', - 'source.zip_url.password': '', - 'source.zip_url.proxy_url': '', - 'source.zip_url.url': '', - 'source.zip_url.ssl.certificate': undefined, - 'source.zip_url.username': '', 'ssl.certificate': '', 'ssl.certificate_authorities': '', 'ssl.key': '', @@ -507,7 +500,6 @@ const payloadData = [ { ...DEFAULT_FIELDS[DataStream.BROWSER], __ui: { - is_zip_url_tls_enabled: false, script_source: { file_name: '', is_generated_script: false, @@ -540,11 +532,6 @@ const payloadData = [ 'source.inline.script': '', 'source.project.content': 'UEsDBBQACAAIAAAAIQAAAAAAAAAAAAAAAAAQAAAAYmFzaWMuam91cm5leS50c2WQQU7DQAxF9znFV8QiUUOmXcCCUMQl2NdMnWbKJDMaO6Ilyt0JASQkNv9Z1teTZWNAIqwP5kU4iZGOug863u7uDXsSddbIddCOl0kMX6iPnsVoOAYxryTO1ucwpoGvtUrm+hiSYsLProIoxwp8iWwVM9oUeuTP/9V5k7UhofCscNhj2yx4xN2CzabElOHXWRxsx/YNroU69QwniImFB8Vui5vJzYcKxYRIJ66WTNQL5hL7p1WD9aYi9zQOtgPFGPNqecJ1sCj+tAB6J6erpj4FDcW3qh6TL5u1Mq/8yjn7BFBLBwhGDIWc4QAAAEkBAABQSwECLQMUAAgACAAAACEARgyFnOEAAABJAQAAEAAAAAAAAAAAACAApIEAAAAAYmFzaWMuam91cm5leS50c1BLBQYAAAAAAQABAD4AAAAfAQAAAAA=', - 'source.zip_url.folder': '', - 'source.zip_url.password': '', - 'source.zip_url.proxy_url': '', - 'source.zip_url.url': '', - 'source.zip_url.username': '', 'ssl.certificate': '', 'ssl.certificate_authorities': '', 'ssl.key': '', diff --git a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter_legacy.test.ts b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter_legacy.test.ts index 6d449132b25d5..c284e18dac56d 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter_legacy.test.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter_legacy.test.ts @@ -514,7 +514,6 @@ const payloadData = [ { ...DEFAULT_FIELDS[DataStream.BROWSER], __ui: { - is_zip_url_tls_enabled: false, script_source: { file_name: '', is_generated_script: false, @@ -547,12 +546,6 @@ const payloadData = [ 'source.inline.script': '', 'source.project.content': 'UEsDBBQACAAIAAAAIQAAAAAAAAAAAAAAAAAQAAAAYmFzaWMuam91cm5leS50c2WQQU7DQAxF9znFV8QiUUOmXcCCUMQl2NdMnWbKJDMaO6Ilyt0JASQkNv9Z1teTZWNAIqwP5kU4iZGOug863u7uDXsSddbIddCOl0kMX6iPnsVoOAYxryTO1ucwpoGvtUrm+hiSYsLProIoxwp8iWwVM9oUeuTP/9V5k7UhofCscNhj2yx4xN2CzabElOHXWRxsx/YNroU69QwniImFB8Vui5vJzYcKxYRIJ66WTNQL5hL7p1WD9aYi9zQOtgPFGPNqecJ1sCj+tAB6J6erpj4FDcW3qh6TL5u1Mq/8yjn7BFBLBwhGDIWc4QAAAEkBAABQSwECLQMUAAgACAAAACEARgyFnOEAAABJAQAAEAAAAAAAAAAAACAApIEAAAAAYmFzaWMuam91cm5leS50c1BLBQYAAAAAAQABAD4AAAAfAQAAAAA=', - 'source.zip_url.folder': '', - 'source.zip_url.password': '', - 'source.zip_url.proxy_url': '', - 'source.zip_url.url': '', - 'source.zip_url.ssl.certificate': undefined, - 'source.zip_url.username': '', 'ssl.certificate': '', 'ssl.certificate_authorities': '', 'ssl.key': '', @@ -576,7 +569,6 @@ const payloadData = [ { ...DEFAULT_FIELDS[DataStream.BROWSER], __ui: { - is_zip_url_tls_enabled: false, script_source: { file_name: '', is_generated_script: false, @@ -609,11 +601,6 @@ const payloadData = [ 'source.inline.script': '', 'source.project.content': 'UEsDBBQACAAIAAAAIQAAAAAAAAAAAAAAAAAQAAAAYmFzaWMuam91cm5leS50c2WQQU7DQAxF9znFV8QiUUOmXcCCUMQl2NdMnWbKJDMaO6Ilyt0JASQkNv9Z1teTZWNAIqwP5kU4iZGOug863u7uDXsSddbIddCOl0kMX6iPnsVoOAYxryTO1ucwpoGvtUrm+hiSYsLProIoxwp8iWwVM9oUeuTP/9V5k7UhofCscNhj2yx4xN2CzabElOHXWRxsx/YNroU69QwniImFB8Vui5vJzYcKxYRIJ66WTNQL5hL7p1WD9aYi9zQOtgPFGPNqecJ1sCj+tAB6J6erpj4FDcW3qh6TL5u1Mq/8yjn7BFBLBwhGDIWc4QAAAEkBAABQSwECLQMUAAgACAAAACEARgyFnOEAAABJAQAAEAAAAAAAAAAAACAApIEAAAAAYmFzaWMuam91cm5leS50c1BLBQYAAAAAAQABAD4AAAAfAQAAAAA=', - 'source.zip_url.folder': '', - 'source.zip_url.password': '', - 'source.zip_url.proxy_url': '', - 'source.zip_url.url': '', - 'source.zip_url.username': '', 'ssl.certificate': '', 'ssl.certificate_authorities': '', 'ssl.key': '', diff --git a/x-pack/plugins/synthetics/server/synthetics_service/utils/secrets.ts b/x-pack/plugins/synthetics/server/synthetics_service/utils/secrets.ts index ed59f2043c9d7..d9a338e1767d9 100644 --- a/x-pack/plugins/synthetics/server/synthetics_service/utils/secrets.ts +++ b/x-pack/plugins/synthetics/server/synthetics_service/utils/secrets.ts @@ -27,15 +27,23 @@ export function formatSecrets(monitor: SyntheticsMonitor): SyntheticsMonitorWith export function normalizeSecrets( monitor: SavedObject ): SavedObject { - const defaultFields = DEFAULT_FIELDS[monitor.attributes[ConfigKey.MONITOR_TYPE]]; + const attributes = normalizeMonitorSecretAttributes(monitor.attributes); const normalizedMonitor = { ...monitor, - attributes: { - ...defaultFields, - ...monitor.attributes, - ...JSON.parse(monitor.attributes.secrets || ''), - }, + attributes, }; - delete normalizedMonitor.attributes.secrets; return normalizedMonitor; } + +export function normalizeMonitorSecretAttributes( + monitor: SyntheticsMonitorWithSecrets +): SyntheticsMonitor { + const defaultFields = DEFAULT_FIELDS[monitor[ConfigKey.MONITOR_TYPE]]; + const normalizedMonitorAttributes = { + ...defaultFields, + ...monitor, + ...JSON.parse(monitor.secrets || ''), + }; + delete normalizedMonitorAttributes.secrets; + return normalizedMonitorAttributes; +} diff --git a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json index a4c73bac176e8..517c52ed4fb71 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -2762,6 +2762,9 @@ "opentelemetry/ruby": { "type": "long" }, + "opentelemetry/rust": { + "type": "long" + }, "opentelemetry/swift": { "type": "long" }, diff --git a/x-pack/plugins/transform/common/privilege/has_privilege_factory.test.ts b/x-pack/plugins/transform/common/privilege/has_privilege_factory.test.ts new file mode 100644 index 0000000000000..c00e79220e5b0 --- /dev/null +++ b/x-pack/plugins/transform/common/privilege/has_privilege_factory.test.ts @@ -0,0 +1,159 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { extractMissingPrivileges, getPrivilegesAndCapabilities } from './has_privilege_factory'; + +describe('has_privilege_factory', () => { + const fullClusterPrivileges = { + 'cluster:admin/transform/preview': true, + 'cluster:admin/transform/put': true, + 'cluster:monitor/transform/get': true, + 'cluster:admin/transform/start': true, + 'cluster:admin/transform/delete': true, + 'cluster:admin/transform/reset': true, + 'cluster:admin/transform/stop': true, + 'cluster:admin/transform/start_task': true, + 'cluster:monitor/transform/stats/get': true, + }; + + const monitorOnlyClusterPrivileges = { + 'cluster:admin/transform/preview': false, + 'cluster:admin/transform/put': false, + 'cluster:admin/transform/start': false, + 'cluster:admin/transform/delete': false, + 'cluster:admin/transform/reset': false, + 'cluster:admin/transform/stop': false, + 'cluster:admin/transform/start_task': false, + 'cluster:monitor/transform/get': true, + 'cluster:monitor/transform/stats/get': true, + }; + const noClusterPrivileges = { + 'cluster:admin/transform/preview': false, + 'cluster:admin/transform/put': false, + 'cluster:admin/transform/start': false, + 'cluster:admin/transform/delete': false, + 'cluster:admin/transform/reset': false, + 'cluster:admin/transform/stop': false, + 'cluster:admin/transform/start_task': false, + 'cluster:monitor/transform/get': false, + 'cluster:monitor/transform/stats/get': false, + }; + + const monitorOnlyMissingPrivileges = Object.entries(monitorOnlyClusterPrivileges) + .filter(([, authorized]) => !authorized) + .map(([priv]) => priv); + + describe('extractMissingPrivileges', () => { + it('returns no missing privilege if provided both monitor and admin cluster privileges', () => { + expect(extractMissingPrivileges(fullClusterPrivileges)).toEqual([]); + }); + it('returns missing privilege if provided only monitor cluster privileges', () => { + expect(extractMissingPrivileges(monitorOnlyClusterPrivileges)).toEqual( + monitorOnlyMissingPrivileges + ); + }); + it('returns all missing privilege if provided no cluster privilege', () => { + const allPrivileges = Object.keys(noClusterPrivileges); + const extracted = extractMissingPrivileges(noClusterPrivileges); + expect(extracted).toEqual(allPrivileges); + }); + }); + + describe('getPrivilegesAndCapabilities', () => { + it('returns full capabilities if provided both monitor and admin cluster privileges', () => { + const fullCapabilities = { + canCreateTransform: true, + canCreateTransformAlerts: true, + canDeleteTransform: true, + canGetTransform: true, + canPreviewTransform: true, + canResetTransform: true, + canScheduleNowTransform: true, + canStartStopTransform: true, + canUseTransformAlerts: true, + }; + + expect(getPrivilegesAndCapabilities(fullClusterPrivileges, true, true)).toEqual({ + capabilities: fullCapabilities, + privileges: { hasAllPrivileges: true, missingPrivileges: { cluster: [], index: [] } }, + }); + expect(getPrivilegesAndCapabilities(fullClusterPrivileges, false, true)).toEqual({ + capabilities: fullCapabilities, + privileges: { + hasAllPrivileges: true, + missingPrivileges: { cluster: [], index: ['monitor'] }, + }, + }); + }); + it('returns view only capabilities if provided only monitor cluster privileges', () => { + const viewOnlyCapabilities = { + canCreateTransform: false, + canCreateTransformAlerts: false, + canDeleteTransform: false, + canGetTransform: true, + canPreviewTransform: false, + canResetTransform: false, + canScheduleNowTransform: false, + canStartStopTransform: false, + canUseTransformAlerts: true, + }; + + const { capabilities, privileges } = getPrivilegesAndCapabilities( + monitorOnlyClusterPrivileges, + true, + false + ); + expect(capabilities).toEqual(viewOnlyCapabilities); + expect(privileges).toEqual({ + hasAllPrivileges: false, + missingPrivileges: { + cluster: monitorOnlyMissingPrivileges, + index: [], + }, + }); + }); + it('returns no capabilities and all the missing privileges if no cluster privileges', () => { + const noCapabilities = { + canCreateTransform: false, + canCreateTransformAlerts: false, + canDeleteTransform: false, + canGetTransform: false, + canPreviewTransform: false, + canResetTransform: false, + canScheduleNowTransform: false, + canStartStopTransform: false, + canUseTransformAlerts: false, + }; + + const { capabilities, privileges } = getPrivilegesAndCapabilities( + noClusterPrivileges, + false, + false + ); + expect(capabilities).toEqual(noCapabilities); + expect(privileges).toEqual({ + hasAllPrivileges: false, + missingPrivileges: { + cluster: Object.keys(noClusterPrivileges), + index: ['monitor'], + }, + }); + }); + + it('returns canResetTransform:false if no cluster privilege for transform/reset', () => { + const { capabilities } = getPrivilegesAndCapabilities( + { + ...fullClusterPrivileges, + 'cluster:admin/transform/reset': false, + }, + false, + false + ); + expect(capabilities.canResetTransform).toEqual(false); + }); + }); +}); diff --git a/x-pack/plugins/transform/public/app/lib/authorization/components/common.ts b/x-pack/plugins/transform/common/privilege/has_privilege_factory.ts similarity index 58% rename from x-pack/plugins/transform/public/app/lib/authorization/components/common.ts rename to x-pack/plugins/transform/common/privilege/has_privilege_factory.ts index e21633ef2de2c..f9afe59355c01 100644 --- a/x-pack/plugins/transform/public/app/lib/authorization/components/common.ts +++ b/x-pack/plugins/transform/common/privilege/has_privilege_factory.ts @@ -8,9 +8,11 @@ import { i18n } from '@kbn/i18n'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; -import { Privileges } from '../../../../../common/types/privileges'; +import { cloneDeep } from 'lodash'; +import { APP_INDEX_PRIVILEGES } from '../constants'; +import { Privileges } from '../types/privileges'; -export interface Capabilities { +export interface TransformCapabilities { canGetTransform: boolean; canDeleteTransform: boolean; canPreviewTransform: boolean; @@ -21,6 +23,19 @@ export interface Capabilities { canUseTransformAlerts: boolean; canResetTransform: boolean; } +export type Capabilities = { [k in keyof TransformCapabilities]: boolean }; + +export const INITIAL_CAPABILITIES = Object.freeze({ + canGetTransform: false, + canDeleteTransform: false, + canPreviewTransform: false, + canCreateTransform: false, + canScheduleNowTransform: false, + canStartStopTransform: false, + canCreateTransformAlerts: false, + canUseTransformAlerts: false, + canResetTransform: false, +}); export type Privilege = [string, string]; @@ -58,10 +73,69 @@ export const hasPrivilegeFactory = ); }; +export const extractMissingPrivileges = ( + privilegesObject: { [key: string]: boolean } = {} +): string[] => + Object.keys(privilegesObject).reduce((privileges: string[], privilegeName: string): string[] => { + if (!privilegesObject[privilegeName]) { + privileges.push(privilegeName); + } + return privileges; + }, []); + +export const getPrivilegesAndCapabilities = ( + clusterPrivileges: Record, + hasOneIndexWithAllPrivileges: boolean, + hasAllPrivileges: boolean +) => { + const privilegesResult: Privileges = { + hasAllPrivileges: true, + missingPrivileges: { + cluster: [], + index: [], + }, + }; + + // Find missing cluster privileges and set overall app privileges + privilegesResult.missingPrivileges.cluster = extractMissingPrivileges(clusterPrivileges); + privilegesResult.hasAllPrivileges = hasAllPrivileges; + + if (!hasOneIndexWithAllPrivileges) { + privilegesResult.missingPrivileges.index = [...APP_INDEX_PRIVILEGES]; + } + + const hasPrivilege = hasPrivilegeFactory(privilegesResult); + + const capabilities = cloneDeep(INITIAL_CAPABILITIES); + capabilities.canGetTransform = + hasPrivilege(['cluster', 'cluster:monitor/transform/get']) && + hasPrivilege(['cluster', 'cluster:monitor/transform/stats/get']); + + capabilities.canCreateTransform = hasPrivilege(['cluster', 'cluster:admin/transform/put']); + + capabilities.canDeleteTransform = hasPrivilege(['cluster', 'cluster:admin/transform/delete']); + + capabilities.canResetTransform = hasPrivilege(['cluster', 'cluster:admin/transform/reset']); + + capabilities.canPreviewTransform = hasPrivilege(['cluster', 'cluster:admin/transform/preview']); + + capabilities.canStartStopTransform = + hasPrivilege(['cluster', 'cluster:admin/transform/start']) && + hasPrivilege(['cluster', 'cluster:admin/transform/start_task']) && + hasPrivilege(['cluster', 'cluster:admin/transform/stop']); + + capabilities.canCreateTransformAlerts = capabilities.canCreateTransform; + + capabilities.canUseTransformAlerts = capabilities.canGetTransform; + + capabilities.canScheduleNowTransform = capabilities.canStartStopTransform; + + return { privileges: privilegesResult, capabilities }; +}; // create the text for button's tooltips if the user // doesn't have the permission to press that button export function createCapabilityFailureMessage( - capability: keyof Capabilities | 'noTransformNodes' + capability: keyof TransformCapabilities | 'noTransformNodes' ) { let message = ''; diff --git a/x-pack/plugins/transform/public/app/lib/authorization/components/authorization_provider.tsx b/x-pack/plugins/transform/public/app/lib/authorization/components/authorization_provider.tsx index 6dbd6e5032276..65a3b2336a114 100644 --- a/x-pack/plugins/transform/public/app/lib/authorization/components/authorization_provider.tsx +++ b/x-pack/plugins/transform/public/app/lib/authorization/components/authorization_provider.tsx @@ -11,27 +11,18 @@ import { Privileges } from '../../../../../common/types/privileges'; import { useRequest } from '../../../hooks'; -import { hasPrivilegeFactory, Capabilities } from './common'; +import { + TransformCapabilities, + INITIAL_CAPABILITIES, +} from '../../../../../common/privilege/has_privilege_factory'; interface Authorization { isLoading: boolean; apiError: Error | null; privileges: Privileges; - capabilities: Capabilities; + capabilities: TransformCapabilities; } -const initialCapabilities: Capabilities = { - canGetTransform: false, - canDeleteTransform: false, - canPreviewTransform: false, - canCreateTransform: false, - canScheduleNowTransform: false, - canStartStopTransform: false, - canCreateTransformAlerts: false, - canUseTransformAlerts: false, - canResetTransform: false, -}; - const initialValue: Authorization = { isLoading: true, apiError: null, @@ -39,7 +30,7 @@ const initialValue: Authorization = { hasAllPrivileges: false, missingPrivileges: {}, }, - capabilities: initialCapabilities, + capabilities: INITIAL_CAPABILITIES, }; export const AuthorizationContext = createContext({ ...initialValue }); @@ -61,42 +52,11 @@ export const AuthorizationProvider = ({ privilegesEndpoint, children }: Props) = const value = { isLoading, - privileges: isLoading ? { ...initialValue.privileges } : privilegesData, - capabilities: { ...initialCapabilities }, + privileges: isLoading ? { ...initialValue.privileges } : privilegesData.privileges, + capabilities: isLoading ? { ...INITIAL_CAPABILITIES } : privilegesData.capabilities, apiError: error ? (error as Error) : null, }; - const hasPrivilege = hasPrivilegeFactory(value.privileges); - - value.capabilities.canGetTransform = - hasPrivilege(['cluster', 'cluster:monitor/transform/get']) && - hasPrivilege(['cluster', 'cluster:monitor/transform/stats/get']); - - value.capabilities.canCreateTransform = hasPrivilege(['cluster', 'cluster:admin/transform/put']); - - value.capabilities.canDeleteTransform = hasPrivilege([ - 'cluster', - 'cluster:admin/transform/delete', - ]); - - value.capabilities.canResetTransform = hasPrivilege(['cluster', 'cluster:admin/transform/reset']); - - value.capabilities.canPreviewTransform = hasPrivilege([ - 'cluster', - 'cluster:admin/transform/preview', - ]); - - value.capabilities.canStartStopTransform = - hasPrivilege(['cluster', 'cluster:admin/transform/start']) && - hasPrivilege(['cluster', 'cluster:admin/transform/start_task']) && - hasPrivilege(['cluster', 'cluster:admin/transform/stop']); - - value.capabilities.canCreateTransformAlerts = value.capabilities.canCreateTransform; - - value.capabilities.canUseTransformAlerts = value.capabilities.canGetTransform; - - value.capabilities.canScheduleNowTransform = value.capabilities.canStartStopTransform; - return ( {children} ); diff --git a/x-pack/plugins/transform/public/app/lib/authorization/components/index.ts b/x-pack/plugins/transform/public/app/lib/authorization/components/index.ts index edc2ec9188cd0..cb0f248efc165 100644 --- a/x-pack/plugins/transform/public/app/lib/authorization/components/index.ts +++ b/x-pack/plugins/transform/public/app/lib/authorization/components/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -export { createCapabilityFailureMessage } from './common'; +export { createCapabilityFailureMessage } from '../../../../../common/privilege/has_privilege_factory'; export { AuthorizationProvider, AuthorizationContext } from './authorization_provider'; export { PrivilegesWrapper } from './with_privileges'; export { NotAuthorizedSection } from './not_authorized_section'; diff --git a/x-pack/plugins/transform/public/app/lib/authorization/components/with_privileges.tsx b/x-pack/plugins/transform/public/app/lib/authorization/components/with_privileges.tsx index 2bbcf84d4623c..2117591142b26 100644 --- a/x-pack/plugins/transform/public/app/lib/authorization/components/with_privileges.tsx +++ b/x-pack/plugins/transform/public/app/lib/authorization/components/with_privileges.tsx @@ -21,7 +21,11 @@ import { SectionLoading } from '../../../components'; import { AuthorizationContext } from './authorization_provider'; import { NotAuthorizedSection } from './not_authorized_section'; -import { hasPrivilegeFactory, toArray, Privilege } from './common'; +import { + hasPrivilegeFactory, + toArray, + Privilege, +} from '../../../../../common/privilege/has_privilege_factory'; interface Props { /** diff --git a/x-pack/plugins/transform/server/capabilities.ts b/x-pack/plugins/transform/server/capabilities.ts new file mode 100644 index 0000000000000..73889a0808359 --- /dev/null +++ b/x-pack/plugins/transform/server/capabilities.ts @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { CoreSetup } from '@kbn/core-lifecycle-server'; +import { SecurityPluginSetup } from '@kbn/security-plugin/server'; +import { + getPrivilegesAndCapabilities, + INITIAL_CAPABILITIES, +} from '../common/privilege/has_privilege_factory'; +import { APP_CLUSTER_PRIVILEGES } from '../common/constants'; +import type { PluginStartDependencies } from './types'; + +export const TRANSFORM_PLUGIN_ID = 'transform' as const; + +export const setupCapabilities = ( + core: Pick, 'capabilities' | 'getStartServices'>, + securitySetup?: SecurityPluginSetup +) => { + core.capabilities.registerProvider(() => { + return { + transform: INITIAL_CAPABILITIES, + }; + }); + + core.capabilities.registerSwitcher(async (request, capabilities, useDefaultCapabilities) => { + if (useDefaultCapabilities) { + return {}; + } + + const isSecurityPluginEnabled = securitySetup?.license.isEnabled() ?? false; + const startServices = await core.getStartServices(); + const [, { security: securityStart }] = startServices; + + // If security is not enabled or not available, transform should have full permission + if (!isSecurityPluginEnabled || !securityStart) { + return { + transform: Object.keys(INITIAL_CAPABILITIES).reduce>((acc, p) => { + acc[p] = true; + return acc; + }, {}), + }; + } + + const checkPrivileges = securityStart.authz.checkPrivilegesDynamicallyWithRequest(request); + + const { hasAllRequested, privileges } = await checkPrivileges({ + elasticsearch: { + cluster: APP_CLUSTER_PRIVILEGES, + index: {}, + }, + }); + + const clusterPrivileges: Record = Array.isArray( + privileges?.elasticsearch?.cluster + ) + ? privileges.elasticsearch.cluster.reduce>((acc, p) => { + acc[p.privilege] = p.authorized; + return acc; + }, {}) + : {}; + + const hasOneIndexWithAllPrivileges = false; + + const transformCapabilities = getPrivilegesAndCapabilities( + clusterPrivileges, + hasOneIndexWithAllPrivileges, + hasAllRequested + ).capabilities; + + return { transform: transformCapabilities }; + }); +}; diff --git a/x-pack/plugins/transform/server/plugin.ts b/x-pack/plugins/transform/server/plugin.ts index b46d7441ee8a9..ab2bb0769e002 100644 --- a/x-pack/plugins/transform/server/plugin.ts +++ b/x-pack/plugins/transform/server/plugin.ts @@ -10,6 +10,7 @@ import { CoreSetup, CoreStart, Plugin, Logger, PluginInitializerContext } from ' import { LicenseType } from '@kbn/licensing-plugin/common/types'; +import { setupCapabilities } from './capabilities'; import { PluginSetupDependencies, PluginStartDependencies } from './types'; import { registerRoutes } from './routes'; import { License } from './services'; @@ -36,9 +37,13 @@ export class TransformServerPlugin implements Plugin<{}, void, any, any> { } setup( - { http, getStartServices, elasticsearch }: CoreSetup, - { licensing, features, alerting }: PluginSetupDependencies + coreSetup: CoreSetup, + { licensing, features, alerting, security: securitySetup }: PluginSetupDependencies ): {} { + const { http, getStartServices } = coreSetup; + + setupCapabilities(coreSetup, securitySetup); + features.registerElasticsearchFeature({ id: PLUGIN.id, management: { diff --git a/x-pack/plugins/transform/server/routes/api/privileges.ts b/x-pack/plugins/transform/server/routes/api/privileges.ts index bad077100c83a..0f078044c3c2a 100644 --- a/x-pack/plugins/transform/server/routes/api/privileges.ts +++ b/x-pack/plugins/transform/server/routes/api/privileges.ts @@ -5,10 +5,13 @@ * 2.0. */ +import type { IScopedClusterClient } from '@kbn/core/server'; +import type { SecurityHasPrivilegesResponse } from '@elastic/elasticsearch/lib/api/types'; +import { getPrivilegesAndCapabilities } from '../../../common/privilege/has_privilege_factory'; import { APP_CLUSTER_PRIVILEGES, APP_INDEX_PRIVILEGES } from '../../../common/constants'; -import { Privileges } from '../../../common/types/privileges'; +import type { Privileges } from '../../../common/types/privileges'; -import { RouteDependencies } from '../../types'; +import type { RouteDependencies } from '../../types'; import { addBasePath } from '..'; export function registerPrivilegesRoute({ router, license }: RouteDependencies) { @@ -28,50 +31,42 @@ export function registerPrivilegesRoute({ router, license }: RouteDependencies) return res.ok({ body: privilegesResult }); } - const esClient = (await ctx.core).elasticsearch.client; - // Get cluster privileges - const { has_all_requested: hasAllPrivileges, cluster } = - await esClient.asCurrentUser.security.hasPrivileges({ + const esClient: IScopedClusterClient = (await ctx.core).elasticsearch.client; + + const esClusterPrivilegesReq: Promise = + esClient.asCurrentUser.security.hasPrivileges({ body: { // @ts-expect-error SecurityClusterPrivilege doesn’t contain all the priviledges cluster: APP_CLUSTER_PRIVILEGES, }, }); + const [esClusterPrivileges, userPrivileges] = await Promise.all([ + // Get cluster privileges + esClusterPrivilegesReq, + // // Get all index privileges the user has + esClient.asCurrentUser.security.getUserPrivileges(), + ]); - // Find missing cluster privileges and set overall app privileges - privilegesResult.missingPrivileges.cluster = extractMissingPrivileges(cluster); - privilegesResult.hasAllPrivileges = hasAllPrivileges; - - // Get all index privileges the user has - const { indices } = await esClient.asCurrentUser.security.getUserPrivileges(); + const { has_all_requested: hasAllPrivileges, cluster } = esClusterPrivileges; + const { indices } = userPrivileges; // Check if they have all the required index privileges for at least one index - const oneIndexWithAllPrivileges = indices.find(({ privileges }: { privileges: string[] }) => { - if (privileges.includes('all')) { - return true; - } + const hasOneIndexWithAllPrivileges = + indices.find(({ privileges }: { privileges: string[] }) => { + if (privileges.includes('all')) { + return true; + } - const indexHasAllPrivileges = APP_INDEX_PRIVILEGES.every((privilege) => - privileges.includes(privilege) - ); + const indexHasAllPrivileges = APP_INDEX_PRIVILEGES.every((privilege) => + privileges.includes(privilege) + ); - return indexHasAllPrivileges; - }); + return indexHasAllPrivileges; + }) !== undefined; - // If they don't, return list of required index privileges - if (!oneIndexWithAllPrivileges) { - privilegesResult.missingPrivileges.index = [...APP_INDEX_PRIVILEGES]; - } - - return res.ok({ body: privilegesResult }); + return res.ok({ + body: getPrivilegesAndCapabilities(cluster, hasOneIndexWithAllPrivileges, hasAllPrivileges), + }); }) ); } - -const extractMissingPrivileges = (privilegesObject: { [key: string]: boolean } = {}): string[] => - Object.keys(privilegesObject).reduce((privileges: string[], privilegeName: string): string[] => { - if (!privilegesObject[privilegeName]) { - privileges.push(privilegeName); - } - return privileges; - }, []); diff --git a/x-pack/plugins/transform/server/types.ts b/x-pack/plugins/transform/server/types.ts index 9aa89e3bfebbf..012f819fce88f 100644 --- a/x-pack/plugins/transform/server/types.ts +++ b/x-pack/plugins/transform/server/types.ts @@ -5,24 +5,27 @@ * 2.0. */ -import { IRouter, CoreStart } from '@kbn/core/server'; -import { PluginStart as DataViewsServerPluginStart } from '@kbn/data-views-plugin/server'; -import { LicensingPluginSetup } from '@kbn/licensing-plugin/server'; -import { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server'; +import type { IRouter, CoreStart } from '@kbn/core/server'; +import type { PluginStart as DataViewsServerPluginStart } from '@kbn/data-views-plugin/server'; +import type { LicensingPluginSetup } from '@kbn/licensing-plugin/server'; +import type { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server'; import type { AlertingPlugin } from '@kbn/alerting-plugin/server'; -import { FieldFormatsSetup, FieldFormatsStart } from '@kbn/field-formats-plugin/server'; -import { License } from './services'; +import type { FieldFormatsSetup, FieldFormatsStart } from '@kbn/field-formats-plugin/server'; +import type { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/server'; +import type { License } from './services'; export interface PluginSetupDependencies { licensing: LicensingPluginSetup; features: FeaturesPluginSetup; alerting?: AlertingPlugin['setup']; fieldFormats: FieldFormatsSetup; + security?: SecurityPluginSetup; } export interface PluginStartDependencies { dataViews: DataViewsServerPluginStart; fieldFormats: FieldFormatsStart; + security?: SecurityPluginStart; } export interface RouteDependencies { diff --git a/x-pack/plugins/transform/tsconfig.json b/x-pack/plugins/transform/tsconfig.json index 59e89a7db6cc4..6be7c1581f949 100644 --- a/x-pack/plugins/transform/tsconfig.json +++ b/x-pack/plugins/transform/tsconfig.json @@ -56,7 +56,9 @@ "@kbn/shared-ux-router", "@kbn/saved-objects-management-plugin", "@kbn/saved-objects-finder-plugin", - "@kbn/ml-route-utils" + "@kbn/ml-route-utils", + "@kbn/core-lifecycle-server", + "@kbn/security-plugin" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 2546c196293bc..9bd009ee3533e 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -25906,8 +25906,6 @@ "xpack.observability.page_header.addUptimeDataLink.label": "Accédez à un tutoriel sur l'ajout de données Uptime", "xpack.observability.page_header.addUXDataLink.label": "Accédez à un tutoriel sur l'ajout de données APM d'expérience utilisateur.", "xpack.observability.pageLayout.sideNavTitle": "Observabilité", - "xpack.observability.pages.alertDetails.alertSummary.duration": "Durée", - "xpack.observability.pages.alertDetails.alertSummary.lastStatusUpdate": "Dernière mise à jour du statut", "xpack.observability.profilingElasticsearchPlugin": "Utiliser le plug-in de profileur Elasticsearch", "xpack.observability.resources.documentation": "Documentation", "xpack.observability.resources.forum": "Forum de discussion", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 3b48e9e4550ec..cf4706027eb9a 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -25887,8 +25887,6 @@ "xpack.observability.page_header.addUptimeDataLink.label": "アップタイムデータの追加に関するチュートリアルに移動", "xpack.observability.page_header.addUXDataLink.label": "ユーザーエクスペリエンスAPMデータの追加に関するチュートリアルに移動", "xpack.observability.pageLayout.sideNavTitle": "Observability", - "xpack.observability.pages.alertDetails.alertSummary.duration": "期間", - "xpack.observability.pages.alertDetails.alertSummary.lastStatusUpdate": "前回のステータス更新", "xpack.observability.profilingElasticsearchPlugin": "Elasticsearchプロファイラープラグインを使用", "xpack.observability.resources.documentation": "ドキュメント", "xpack.observability.resources.forum": "ディスカッションフォーラム", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index f33e36ce2815e..e29eaf05fc552 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -25903,8 +25903,6 @@ "xpack.observability.page_header.addUptimeDataLink.label": "导航到有关如何添加 Uptime 数据的教程", "xpack.observability.page_header.addUXDataLink.label": "导航到有关如何添加用户体验 APM 数据的教程", "xpack.observability.pageLayout.sideNavTitle": "Observability", - "xpack.observability.pages.alertDetails.alertSummary.duration": "持续时间", - "xpack.observability.pages.alertDetails.alertSummary.lastStatusUpdate": "上次状态更新", "xpack.observability.profilingElasticsearchPlugin": "使用 Elasticsearch 分析器插件", "xpack.observability.resources.documentation": "文档", "xpack.observability.resources.forum": "讨论论坛", diff --git a/x-pack/plugins/upgrade_assistant/server/routes/node_disk_space.test.ts b/x-pack/plugins/upgrade_assistant/server/routes/node_disk_space.test.ts index e300033f800ba..315c2537afa15 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/node_disk_space.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/node_disk_space.test.ts @@ -113,7 +113,7 @@ describe('Disk space API', () => { .getSettings as jest.Mock ).mockResolvedValue({ defaults: {}, - transient: { 'cluster.routing.allocation.disk.watermark.low': '80%' }, + transient: { 'cluster.routing.allocation.disk.watermark.low': '79%' }, persistent: { 'cluster.routing.allocation.disk.watermark.low': '85%' }, }); @@ -128,7 +128,7 @@ describe('Disk space API', () => { nodeName: 'node_name', nodeId: '1YOaoS9lTNOiTxR1uzSgRA', available: '20%', - lowDiskWatermarkSetting: '80%', + lowDiskWatermarkSetting: '79%', }, ]); }); @@ -186,7 +186,7 @@ describe('Disk space API', () => { .getSettings as jest.Mock ).mockResolvedValue({ defaults: { - 'cluster.routing.allocation.disk.watermark.low': '10%', + 'cluster.routing.allocation.disk.watermark.low': '85%', }, transient: {}, persistent: {}, diff --git a/x-pack/plugins/upgrade_assistant/server/routes/node_disk_space.ts b/x-pack/plugins/upgrade_assistant/server/routes/node_disk_space.ts index 3707bee0f1395..5f275be8b5d73 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/node_disk_space.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/node_disk_space.ts @@ -88,9 +88,10 @@ export function registerNodeDiskSpaceRoute({ router, lib: { handleEsError } }: R const rawLowDiskWatermarkPercentageValue = Number( lowDiskWatermarkSetting!.replace('%', '') ); + // ES interprets this setting as a threshold for used disk space; we want free disk space + const freeDiskSpaceAllocated = 100 - rawLowDiskWatermarkPercentageValue; - // If the percentage available is < the low disk watermark setting, mark node as having low disk space - if (percentageAvailable < rawLowDiskWatermarkPercentageValue) { + if (percentageAvailable < freeDiskSpaceAllocated) { nodesWithLowDiskSpace.push({ nodeId, nodeName: node.name || nodeId, diff --git a/x-pack/plugins/ux/public/components/app/rum_dashboard/rum_home.tsx b/x-pack/plugins/ux/public/components/app/rum_dashboard/rum_home.tsx index 6ed203a65b86e..00c4c2be6d3e0 100644 --- a/x-pack/plugins/ux/public/components/app/rum_dashboard/rum_home.tsx +++ b/x-pack/plugins/ux/public/components/app/rum_dashboard/rum_home.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiTitle, EuiFlexItem } from '@elastic/eui'; import type { NoDataConfig } from '@kbn/shared-ux-page-kibana-template'; +import { EuiSpacer } from '@elastic/eui'; import { WebApplicationSelect } from './panels/web_application_select'; import { UserPercentile } from './user_percentile'; import { useBreakpoints } from '../../../hooks/use_breakpoints'; @@ -86,6 +87,7 @@ function PageHeader() { + diff --git a/x-pack/plugins/ux/public/components/app/rum_dashboard/ux_overview_fetchers.ts b/x-pack/plugins/ux/public/components/app/rum_dashboard/ux_overview_fetchers.ts index c7c05dd93d4eb..e67a78cf8760e 100644 --- a/x-pack/plugins/ux/public/components/app/rum_dashboard/ux_overview_fetchers.ts +++ b/x-pack/plugins/ux/public/components/app/rum_dashboard/ux_overview_fetchers.ts @@ -109,17 +109,23 @@ async function esQuery( dataStartPlugin: DataPublicPluginStart, query: IKibanaSearchRequest & { params: { index?: string } } ) { - return new Promise>((resolve, reject) => { - const search$ = dataStartPlugin.search.search(query).subscribe({ - next: (result) => { - if (isCompleteResponse(result)) { - resolve(result.rawResponse as any); - search$.unsubscribe(); - } - }, - error: (err) => { - reject(err); - }, - }); - }); + return new Promise>( + (resolve, reject) => { + const search$ = dataStartPlugin.search + .search(query, { + legacyHitsTotal: false, + }) + .subscribe({ + next: (result) => { + if (isCompleteResponse(result)) { + resolve(result.rawResponse as any); + search$.unsubscribe(); + } + }, + error: (err) => { + reject(err); + }, + }); + } + ); } diff --git a/x-pack/plugins/ux/public/services/data/core_web_vitals_query.ts b/x-pack/plugins/ux/public/services/data/core_web_vitals_query.ts index 75970a99cba03..5636755a314bb 100644 --- a/x-pack/plugins/ux/public/services/data/core_web_vitals_query.ts +++ b/x-pack/plugins/ux/public/services/data/core_web_vitals_query.ts @@ -31,7 +31,11 @@ const getRanksPercentages = (ranks?: Record) => { }; export function transformCoreWebVitalsResponse( - response?: ESSearchResponse>, + response?: ESSearchResponse< + T, + ReturnType, + { restTotalHitsAsInt: false } + >, percentile = PERCENTILE_DEFAULT ): UXMetrics | undefined { if (!response) return response; diff --git a/x-pack/plugins/ux/public/services/data/has_rum_data_query.ts b/x-pack/plugins/ux/public/services/data/has_rum_data_query.ts index 91710cecf0c88..487c67c17b9ef 100644 --- a/x-pack/plugins/ux/public/services/data/has_rum_data_query.ts +++ b/x-pack/plugins/ux/public/services/data/has_rum_data_query.ts @@ -16,7 +16,11 @@ import { TRANSACTION_PAGE_LOAD } from '../../../common/transaction_types'; import { rangeQuery } from './range_query'; export function formatHasRumResult( - esResult: ESSearchResponse>, + esResult: ESSearchResponse< + T, + ReturnType, + { restTotalHitsAsInt: false } + >, indices?: string ) { if (!esResult) return esResult; diff --git a/x-pack/test/api_integration/apis/asset_manager/tests/assets.ts b/x-pack/test/api_integration/apis/asset_manager/tests/assets.ts index c3f0a7e8f3668..d99fdf9a9746b 100644 --- a/x-pack/test/api_integration/apis/asset_manager/tests/assets.ts +++ b/x-pack/test/api_integration/apis/asset_manager/tests/assets.ts @@ -11,6 +11,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; import { createSampleAssets, deleteSampleAssets, viewSampleAssetDocs } from '../helpers'; const ASSETS_ENDPOINT = '/api/asset-manager/assets'; +const DIFF_ENDPOINT = ASSETS_ENDPOINT + '/diff'; export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); @@ -179,5 +180,137 @@ export default function ({ getService }: FtrProviderContext) { expect(getResponse.body.results).to.eql(targetAssets); }); }); + + describe('GET /assets/diff', () => { + it('should reject requests that do not include the two time ranges to compare', async () => { + const timestamp = new Date().toISOString(); + + let getResponse = await supertest.get(DIFF_ENDPOINT).expect(400); + expect(getResponse.body.message).to.equal( + '[request query.aFrom]: expected value of type [string] but got [undefined]' + ); + + getResponse = await supertest.get(DIFF_ENDPOINT).query({ aFrom: timestamp }).expect(400); + expect(getResponse.body.message).to.equal( + '[request query.aTo]: expected value of type [string] but got [undefined]' + ); + + getResponse = await supertest + .get(DIFF_ENDPOINT) + .query({ aFrom: timestamp, aTo: timestamp }) + .expect(400); + expect(getResponse.body.message).to.equal( + '[request query.bFrom]: expected value of type [string] but got [undefined]' + ); + + getResponse = await supertest + .get(DIFF_ENDPOINT) + .query({ aFrom: timestamp, aTo: timestamp, bFrom: timestamp }) + .expect(400); + expect(getResponse.body.message).to.equal( + '[request query.bTo]: expected value of type [string] but got [undefined]' + ); + + await supertest + .get(DIFF_ENDPOINT) + .query({ aFrom: timestamp, aTo: timestamp, bFrom: timestamp, bTo: timestamp }) + .expect(200); + }); + + it('should reject requests where either time range is moving backwards in time', async () => { + const now = new Date(); + const isoNow = now.toISOString(); + const oneHourAgo = new Date(now.getTime() - 1000 * 60 * 60 * 1).toISOString(); + + let getResponse = await supertest + .get(DIFF_ENDPOINT) + .query({ + aFrom: isoNow, + aTo: oneHourAgo, + bFrom: isoNow, + bTo: isoNow, + }) + .expect(400); + expect(getResponse.body.message).to.equal( + `Time range cannot move backwards in time. "aTo" (${oneHourAgo}) is before "aFrom" (${isoNow}).` + ); + + getResponse = await supertest + .get(DIFF_ENDPOINT) + .query({ + aFrom: isoNow, + aTo: isoNow, + bFrom: isoNow, + bTo: oneHourAgo, + }) + .expect(400); + expect(getResponse.body.message).to.equal( + `Time range cannot move backwards in time. "bTo" (${oneHourAgo}) is before "bFrom" (${isoNow}).` + ); + + await supertest + .get(DIFF_ENDPOINT) + .query({ + aFrom: oneHourAgo, + aTo: isoNow, + bFrom: oneHourAgo, + bTo: isoNow, + }) + .expect(200); + }); + + it('should return the difference in assets present between two time ranges', async () => { + const onlyInA = sampleAssetDocs.slice(0, 2); + const onlyInB = sampleAssetDocs.slice(sampleAssetDocs.length - 2); + const inBoth = sampleAssetDocs.slice(2, sampleAssetDocs.length - 2); + const now = new Date(); + const oneHourAgo = new Date(now.getTime() - 1000 * 60 * 60 * 1); + const twoHoursAgo = new Date(now.getTime() - 1000 * 60 * 60 * 2); + await createSampleAssets(supertest, { + baseDateTime: twoHoursAgo.toISOString(), + excludeEans: inBoth.concat(onlyInB).map((asset) => asset['asset.ean']), + }); + await createSampleAssets(supertest, { + baseDateTime: oneHourAgo.toISOString(), + excludeEans: onlyInA.concat(onlyInB).map((asset) => asset['asset.ean']), + }); + await createSampleAssets(supertest, { + excludeEans: inBoth.concat(onlyInA).map((asset) => asset['asset.ean']), + }); + + const twoHoursAndTenMinuesAgo = new Date(now.getTime() - 1000 * 60 * 130 * 1); + const fiftyMinuesAgo = new Date(now.getTime() - 1000 * 60 * 50 * 1); + const seventyMinuesAgo = new Date(now.getTime() - 1000 * 60 * 70 * 1); + const tenMinutesAfterNow = new Date(now.getTime() + 1000 * 60 * 10); + + const getResponse = await supertest + .get(DIFF_ENDPOINT) + .query({ + aFrom: twoHoursAndTenMinuesAgo, + aTo: fiftyMinuesAgo, + bFrom: seventyMinuesAgo, + bTo: tenMinutesAfterNow, + }) + .expect(200); + + expect(getResponse.body).to.have.property('onlyInA'); + expect(getResponse.body).to.have.property('onlyInB'); + expect(getResponse.body).to.have.property('inBoth'); + + getResponse.body.onlyInA.forEach((asset: any) => { + delete asset['@timestamp']; + }); + getResponse.body.onlyInB.forEach((asset: any) => { + delete asset['@timestamp']; + }); + getResponse.body.inBoth.forEach((asset: any) => { + delete asset['@timestamp']; + }); + + expect(getResponse.body.onlyInA).to.eql(onlyInA); + expect(getResponse.body.onlyInB).to.eql(onlyInB); + expect(getResponse.body.inBoth).to.eql(inBoth); + }); + }); }); } diff --git a/x-pack/test/api_integration/apis/synthetics/add_monitor_project.ts b/x-pack/test/api_integration/apis/synthetics/add_monitor_project.ts index 4bc005c46596c..2dc99487a138e 100644 --- a/x-pack/test/api_integration/apis/synthetics/add_monitor_project.ts +++ b/x-pack/test/api_integration/apis/synthetics/add_monitor_project.ts @@ -144,7 +144,6 @@ export default function ({ getService }: FtrProviderContext) { expect(decryptedCreatedMonitor.body.attributes).to.eql({ __ui: { - is_zip_url_tls_enabled: false, script_source: { file_name: '', is_generated_script: false, @@ -189,11 +188,6 @@ export default function ({ getService }: FtrProviderContext) { }, screenshots: 'on', 'service.name': '', - 'source.zip_url.folder': '', - 'source.zip_url.proxy_url': '', - 'source.zip_url.url': '', - 'source.zip_url.password': '', - 'source.zip_url.username': '', synthetics_args: [], tags: [], 'throttling.config': '5d/3u/20l', diff --git a/x-pack/test/api_integration/apis/synthetics/add_monitor_project_legacy.ts b/x-pack/test/api_integration/apis/synthetics/add_monitor_project_legacy.ts index ff0c1bbf0f9a1..b9b3d567c44aa 100644 --- a/x-pack/test/api_integration/apis/synthetics/add_monitor_project_legacy.ts +++ b/x-pack/test/api_integration/apis/synthetics/add_monitor_project_legacy.ts @@ -129,7 +129,6 @@ export default function ({ getService }: FtrProviderContext) { expect(decryptedCreatedMonitor.body.attributes).to.eql({ __ui: { - is_zip_url_tls_enabled: false, script_source: { file_name: '', is_generated_script: false, @@ -174,11 +173,6 @@ export default function ({ getService }: FtrProviderContext) { }, screenshots: 'on', 'service.name': '', - 'source.zip_url.folder': '', - 'source.zip_url.proxy_url': '', - 'source.zip_url.url': '', - 'source.zip_url.password': '', - 'source.zip_url.username': '', synthetics_args: [], tags: [], 'throttling.config': '5d/3u/20l', diff --git a/x-pack/test/api_integration/apis/synthetics/sample_data/test_browser_policy.ts b/x-pack/test/api_integration/apis/synthetics/sample_data/test_browser_policy.ts index f5acb0eef3f8c..0cebf231cf787 100644 --- a/x-pack/test/api_integration/apis/synthetics/sample_data/test_browser_policy.ts +++ b/x-pack/test/api_integration/apis/synthetics/sample_data/test_browser_policy.ts @@ -160,7 +160,7 @@ export const getTestBrowserSyntheticsPolicy = ({ vars: { __ui: { value: - '{"script_source":{"is_generated_script":false,"file_name":""},"is_zip_url_tls_enabled":false,"is_tls_enabled":false}', + '{"script_source":{"is_generated_script":false,"file_name":""},"is_tls_enabled":false}', type: 'yaml', }, enabled: { value: true, type: 'bool' }, @@ -170,10 +170,10 @@ export const getTestBrowserSyntheticsPolicy = ({ 'service.name': { value: '', type: 'text' }, timeout: { value: '16s', type: 'text' }, tags: { value: '["cookie-test","browser"]', type: 'yaml' }, - 'source.zip_url.url': { value: '', type: 'text' }, - 'source.zip_url.username': { value: '', type: 'text' }, - 'source.zip_url.folder': { value: '', type: 'text' }, - 'source.zip_url.password': { value: '', type: 'password' }, + 'source.zip_url.url': { type: 'text' }, + 'source.zip_url.username': { type: 'text' }, + 'source.zip_url.folder': { type: 'text' }, + 'source.zip_url.password': { type: 'password' }, 'source.inline.script': { value: '"step(\\"Visit /users api route\\", async () => {\\\\n const response = await page.goto(\'https://nextjs-test-synthetics.vercel.app/api/users\');\\\\n expect(response.status()).toEqual(200);\\\\n});"', @@ -188,13 +188,13 @@ export const getTestBrowserSyntheticsPolicy = ({ 'throttling.config': { value: '5d/3u/20l', type: 'text' }, 'filter_journeys.tags': { value: null, type: 'yaml' }, 'filter_journeys.match': { value: null, type: 'text' }, - 'source.zip_url.ssl.certificate_authorities': { value: null, type: 'yaml' }, - 'source.zip_url.ssl.certificate': { value: null, type: 'yaml' }, - 'source.zip_url.ssl.key': { value: null, type: 'yaml' }, - 'source.zip_url.ssl.key_passphrase': { value: null, type: 'text' }, - 'source.zip_url.ssl.verification_mode': { value: null, type: 'text' }, - 'source.zip_url.ssl.supported_protocols': { value: null, type: 'yaml' }, - 'source.zip_url.proxy_url': { value: '', type: 'text' }, + 'source.zip_url.ssl.certificate_authorities': { type: 'yaml' }, + 'source.zip_url.ssl.certificate': { type: 'yaml' }, + 'source.zip_url.ssl.key': { type: 'yaml' }, + 'source.zip_url.ssl.key_passphrase': { type: 'text' }, + 'source.zip_url.ssl.verification_mode': { type: 'text' }, + 'source.zip_url.ssl.supported_protocols': { type: 'yaml' }, + 'source.zip_url.proxy_url': { type: 'text' }, location_name: { value: 'Test private location 0', type: 'text' }, id: { value: id, type: 'text' }, config_id: { value: id, type: 'text' }, @@ -207,7 +207,6 @@ export const getTestBrowserSyntheticsPolicy = ({ compiled_stream: { __ui: { script_source: { is_generated_script: false, file_name: '' }, - is_zip_url_tls_enabled: false, is_tls_enabled: false, }, type: 'browser', diff --git a/x-pack/test/api_integration/apis/synthetics/sample_data/test_project_monitor_policy.ts b/x-pack/test/api_integration/apis/synthetics/sample_data/test_project_monitor_policy.ts index c2a433c602820..cd5ea22451b92 100644 --- a/x-pack/test/api_integration/apis/synthetics/sample_data/test_project_monitor_policy.ts +++ b/x-pack/test/api_integration/apis/synthetics/sample_data/test_project_monitor_policy.ts @@ -185,8 +185,7 @@ export const getTestProjectSyntheticsPolicy = ( }, vars: { __ui: { - value: - '{"script_source":{"is_generated_script":false,"file_name":""},"is_zip_url_tls_enabled":false}', + value: '{"script_source":{"is_generated_script":false,"file_name":""}}', type: 'yaml', }, enabled: { value: true, type: 'bool' }, @@ -196,10 +195,10 @@ export const getTestProjectSyntheticsPolicy = ( 'service.name': { value: '', type: 'text' }, timeout: { value: null, type: 'text' }, tags: { value: null, type: 'yaml' }, - 'source.zip_url.url': { value: '', type: 'text' }, - 'source.zip_url.username': { value: '', type: 'text' }, - 'source.zip_url.folder': { value: '', type: 'text' }, - 'source.zip_url.password': { value: '', type: 'password' }, + 'source.zip_url.url': { type: 'text' }, + 'source.zip_url.username': { type: 'text' }, + 'source.zip_url.folder': { type: 'text' }, + 'source.zip_url.password': { type: 'password' }, 'source.inline.script': { value: null, type: 'yaml' }, 'source.project.content': { value: @@ -217,13 +216,13 @@ export const getTestProjectSyntheticsPolicy = ( 'throttling.config': { value: '5d/3u/20l', type: 'text' }, 'filter_journeys.tags': { value: null, type: 'yaml' }, 'filter_journeys.match': { value: '"check if title is present"', type: 'text' }, - 'source.zip_url.ssl.certificate_authorities': { value: null, type: 'yaml' }, - 'source.zip_url.ssl.certificate': { value: null, type: 'yaml' }, - 'source.zip_url.ssl.key': { value: null, type: 'yaml' }, - 'source.zip_url.ssl.key_passphrase': { value: null, type: 'text' }, - 'source.zip_url.ssl.verification_mode': { value: null, type: 'text' }, - 'source.zip_url.ssl.supported_protocols': { value: null, type: 'yaml' }, - 'source.zip_url.proxy_url': { value: '', type: 'text' }, + 'source.zip_url.ssl.certificate_authorities': { type: 'yaml' }, + 'source.zip_url.ssl.certificate': { type: 'yaml' }, + 'source.zip_url.ssl.key': { type: 'yaml' }, + 'source.zip_url.ssl.key_passphrase': { type: 'text' }, + 'source.zip_url.ssl.verification_mode': { type: 'text' }, + 'source.zip_url.ssl.supported_protocols': { type: 'yaml' }, + 'source.zip_url.proxy_url': { type: 'text' }, location_name: { value: 'Test private location 0', type: 'text' }, id: { value: id, type: 'text' }, config_id: { value: configId, type: 'text' }, @@ -237,7 +236,6 @@ export const getTestProjectSyntheticsPolicy = ( compiled_stream: { __ui: { script_source: { is_generated_script: false, file_name: '' }, - is_zip_url_tls_enabled: false, }, type: 'browser', name: 'check if title is present', diff --git a/x-pack/test/api_integration/apis/uptime/rest/fixtures/browser_monitor.json b/x-pack/test/api_integration/apis/uptime/rest/fixtures/browser_monitor.json index e8776fa1874b4..1a12efba9dc1c 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/fixtures/browser_monitor.json +++ b/x-pack/test/api_integration/apis/uptime/rest/fixtures/browser_monitor.json @@ -21,14 +21,8 @@ "is_generated_script": false, "file_name": "" }, - "is_zip_url_tls_enabled": false, "is_tls_enabled": false }, - "source.zip_url.url": "", - "source.zip_url.username": "", - "source.zip_url.password": "", - "source.zip_url.folder": "", - "source.zip_url.proxy_url": "", "source.inline.script": "step(\"Visit /users api route\", async () => {\\n const response = await page.goto('https://nextjs-test-synthetics.vercel.app/api/users');\\n expect(response.status()).toEqual(200);\\n});", "source.project.content": "", "params": "", diff --git a/x-pack/test/api_integration/apis/uptime/rest/fixtures/tcp_monitor.json b/x-pack/test/api_integration/apis/uptime/rest/fixtures/tcp_monitor.json index 209ef89373736..c3664c5646b16 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/fixtures/tcp_monitor.json +++ b/x-pack/test/api_integration/apis/uptime/rest/fixtures/tcp_monitor.json @@ -11,8 +11,7 @@ "tags": [], "timeout": "16", "__ui": { - "is_tls_enabled": true, - "is_zip_url_tls_enabled": false + "is_tls_enabled": true }, "hosts": "example-host:40", "urls": "example-host:40", diff --git a/x-pack/test/functional_with_es_ssl/apps/discover_ml_uptime/discover/search_source_alert.ts b/x-pack/test/functional_with_es_ssl/apps/discover_ml_uptime/discover/search_source_alert.ts index c9384162fcec3..acccceb2f8b89 100644 --- a/x-pack/test/functional_with_es_ssl/apps/discover_ml_uptime/discover/search_source_alert.ts +++ b/x-pack/test/functional_with_es_ssl/apps/discover_ml_uptime/discover/search_source_alert.ts @@ -307,10 +307,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await titleElem.getAttribute('value')).to.equal(dataView); }; - // FLAKY: https://github.com/elastic/kibana/issues/152477 - // FLAKY: https://github.com/elastic/kibana/issues/152478 - // FLAKY: https://github.com/elastic/kibana/issues/152479 - describe.skip('Search source Alert', () => { + describe('Search source Alert', () => { before(async () => { await security.testUser.setRoles(['discover_alert']); diff --git a/x-pack/test/security_solution_endpoint/services/endpoint.ts b/x-pack/test/security_solution_endpoint/services/endpoint.ts index 6262ae62f8f8f..277977acf95a3 100644 --- a/x-pack/test/security_solution_endpoint/services/endpoint.ts +++ b/x-pack/test/security_solution_endpoint/services/endpoint.ts @@ -116,7 +116,7 @@ export class EndpointTestResources extends FtrService { customIndexFn, } = options; - if (waitUntilTransformed) { + if (waitUntilTransformed && customIndexFn) { // need this before indexing docs so that the united transform doesn't // create a checkpoint with a timestamp after the doc timestamps await this.stopTransform(metadataTransformPrefix); @@ -139,15 +139,17 @@ export class EndpointTestResources extends FtrService { alertsPerHost, enableFleetIntegration, undefined, - CurrentKibanaVersionDocGenerator, - false + CurrentKibanaVersionDocGenerator ); - if (waitUntilTransformed) { + if (waitUntilTransformed && customIndexFn) { await this.startTransform(metadataTransformPrefix); const metadataIds = Array.from(new Set(indexedData.hosts.map((host) => host.agent.id))); await this.waitForEndpoints(metadataIds, waitTimeout); await this.startTransform(METADATA_UNITED_TRANSFORM); + } + + if (waitUntilTransformed) { const agentIds = Array.from(new Set(indexedData.agents.map((agent) => agent.agent!.id))); await this.waitForUnitedEndpoints(agentIds, waitTimeout); }