diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index c10bf18e5e917..eb7daad21b0f0 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -117,6 +117,7 @@ enabled: - x-pack/test/alerting_api_integration/basic/config.ts - x-pack/test/alerting_api_integration/security_and_spaces/group1/config.ts - x-pack/test/alerting_api_integration/security_and_spaces/group2/config.ts + - x-pack/test/alerting_api_integration/security_and_spaces/group2/config_non_dedicated_task_runner.ts - x-pack/test/alerting_api_integration/spaces_only/config.ts - x-pack/test/api_integration_basic/config.ts - x-pack/test/api_integration/config_security_basic.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1b158709e73df..d4bf323f938a7 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -13,8 +13,10 @@ /src/plugins/saved_search/ @elastic/kibana-data-discovery /x-pack/plugins/discover_enhanced/ @elastic/kibana-data-discovery /test/functional/apps/discover/ @elastic/kibana-data-discovery +/test/api_integration/apis/unified_field_list/ @elastic/kibana-data-discovery /x-pack/plugins/graph/ @elastic/kibana-data-discovery /x-pack/test/functional/apps/graph @elastic/kibana-data-discovery +/src/plugins/unified_field_list/ @elastic/kibana-data-discovery # Vis Editors /x-pack/plugins/lens/ @elastic/kibana-vis-editors @@ -41,7 +43,10 @@ /src/plugins/url_forwarding/ @elastic/kibana-vis-editors /packages/kbn-tinymath/ @elastic/kibana-vis-editors /x-pack/test/functional/apps/lens @elastic/kibana-vis-editors +/x-pack/test/api_integration/apis/lens/ @elastic/kibana-vis-editors /test/functional/apps/visualize/ @elastic/kibana-vis-editors +/src/plugins/unified_field_list/ @elastic/kibana-vis-editors +/test/api_integration/apis/unified_field_list/ @elastic/kibana-vis-editors # Application Services /examples/bfetch_explorer/ @elastic/kibana-app-services @@ -87,6 +92,7 @@ /x-pack/test/search_sessions_integration/ @elastic/kibana-app-services /src/plugins/dashboard/public/application/embeddable/viewport/print_media @elastic/kibana-app-services x-pack/plugins/files @elastic/kibana-app-services +x-pack/examples/files_example @elastic/kibana-app-services ### Observability Plugins @@ -177,6 +183,7 @@ x-pack/plugins/files @elastic/kibana-app-services /src/plugins/controls/ @elastic/kibana-presentation /test/functional/apps/dashboard/ @elastic/kibana-presentation /test/functional/apps/dashboard_elements/ @elastic/kibana-presentation +/test/functional/services/dashboard/ @elastic/kibana-presentation /x-pack/plugins/canvas/ @elastic/kibana-presentation /x-pack/plugins/dashboard_enhanced/ @elastic/kibana-presentation /x-pack/test/functional/apps/canvas/ @elastic/kibana-presentation @@ -618,6 +625,7 @@ x-pack/test/threat_intelligence_cypress @elastic/protections-experience # Cloud Security Posture /x-pack/plugins/cloud_security_posture/ @elastic/kibana-cloud-security-posture /x-pack/plugins/security_solution/public/cloud_security_posture @elastic/kibana-cloud-security-posture +/x-pack/test/api_integration/apis/cloud_security_posture/ @elastic/kibana-cloud-security-posture # Security Solution onboarding tour /x-pack/plugins/security_solution/public/common/components/guided_onboarding @elastic/platform-onboarding diff --git a/.i18nrc.json b/.i18nrc.json index 28986568d5bf3..2a301de5e7edf 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -91,7 +91,8 @@ "visTypeVislib": "src/plugins/vis_types/vislib", "visTypeXy": "src/plugins/vis_types/xy", "visualizations": "src/plugins/visualizations", - "unifiedSearch": "src/plugins/unified_search" + "unifiedSearch": "src/plugins/unified_search", + "unifiedFieldList": "src/plugins/unified_field_list" }, "translations": [] } diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index b61c1c9ee82c2..c3ec6ff5cea3e 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: 2022-08-19 +date: 2022-08-23 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 809e93ae17867..919ab9152d9d1 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: 2022-08-19 +date: 2022-08-23 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 6bfdb44ca8283..ceff930ca4229 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: 2022-08-19 +date: 2022-08-23 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 1a352489b23b2..ca578fd099f4e 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: 2022-08-19 +date: 2022-08-23 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 7bc4b48eb4e30..0c1e93866ca04 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index b468619a5a7c1..f92b26599d72e 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners'] --- import bannersObj from './banners.devdocs.json'; diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx index 1ebf19dd6f19c..49bd8b22d15ca 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'bfetch'] --- import bfetchObj from './bfetch.devdocs.json'; diff --git a/api_docs/canvas.mdx b/api_docs/canvas.mdx index bc706c9726e05..49e969a447fda 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: 2022-08-19 +date: 2022-08-23 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 03bb0a4dfe696..41d48c3884216 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: 2022-08-19 +date: 2022-08-23 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 2b2a3a629f97e..a4cd6c9d09965 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: 2022-08-19 +date: 2022-08-23 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 5190fbb44a346..0248f43cc8e12 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index 592367f1d8704..a957d84ea6932 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: 2022-08-19 +date: 2022-08-23 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 1037cc9e9c3f2..c32c221f4a597 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/controls.devdocs.json b/api_docs/controls.devdocs.json index 6ebca7b46dd34..e7a02b67fc192 100644 --- a/api_docs/controls.devdocs.json +++ b/api_docs/controls.devdocs.json @@ -1906,7 +1906,7 @@ }, ">" ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx", "deprecated": false, "children": [ { @@ -1919,7 +1919,7 @@ "signature": [ "\"rangeSliderControl\"" ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx", "deprecated": false }, { @@ -1929,7 +1929,7 @@ "tags": [], "label": "deferEmbeddableLoad", "description": [], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx", "deprecated": false }, { @@ -1942,7 +1942,7 @@ "signature": [ "any" ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx", "deprecated": false, "children": [ { @@ -1961,7 +1961,7 @@ "text": "ReduxEmbeddablePackage" } ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx", "deprecated": false, "isRequired": true }, @@ -1981,7 +1981,7 @@ "text": "RangeSliderEmbeddableInput" } ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx", "deprecated": false, "isRequired": true }, @@ -2001,7 +2001,7 @@ "text": "ControlOutput" } ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx", "deprecated": false, "isRequired": true }, @@ -2038,7 +2038,7 @@ }, "> | undefined" ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx", "deprecated": false, "isRequired": false } @@ -2055,7 +2055,7 @@ "signature": [ "() => void" ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx", "deprecated": false, "children": [], "returnComment": [] @@ -2070,7 +2070,7 @@ "signature": [ "() => void" ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx", "deprecated": false, "children": [], "returnComment": [] @@ -2085,7 +2085,7 @@ "signature": [ "(node: HTMLElement) => void" ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx", "deprecated": false, "children": [ { @@ -2098,7 +2098,7 @@ "signature": [ "HTMLElement" ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx", "deprecated": false, "isRequired": true } @@ -2191,7 +2191,7 @@ }, ">" ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx", "deprecated": false, "children": [ { @@ -2201,9 +2201,54 @@ "tags": [], "label": "type", "description": [], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx", "deprecated": false }, + { + "parentPluginId": "controls", + "id": "def-public.RangeSliderEmbeddableFactory.getDisplayName", + "type": "Function", + "tags": [], + "label": "getDisplayName", + "description": [], + "signature": [ + "() => string" + ], + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "controls", + "id": "def-public.RangeSliderEmbeddableFactory.getDescription", + "type": "Function", + "tags": [], + "label": "getDescription", + "description": [], + "signature": [ + "() => string" + ], + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "controls", + "id": "def-public.RangeSliderEmbeddableFactory.getIconType", + "type": "Function", + "tags": [], + "label": "getIconType", + "description": [], + "signature": [ + "() => string" + ], + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx", + "deprecated": false, + "children": [], + "returnComment": [] + }, { "parentPluginId": "controls", "id": "def-public.RangeSliderEmbeddableFactory.canCreateNew", @@ -2214,22 +2259,22 @@ "signature": [ "() => boolean" ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx", "deprecated": false, "children": [], "returnComment": [] }, { "parentPluginId": "controls", - "id": "def-public.RangeSliderEmbeddableFactory.Unnamed", + "id": "def-public.RangeSliderEmbeddableFactory.isEditable", "type": "Function", "tags": [], - "label": "Constructor", + "label": "isEditable", "description": [], "signature": [ - "any" + "() => Promise" ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx", "deprecated": false, "children": [], "returnComment": [] @@ -2284,7 +2329,7 @@ }, ">" ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx", "deprecated": false, "children": [ { @@ -2303,7 +2348,7 @@ "text": "RangeSliderEmbeddableInput" } ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx", "deprecated": false, "isRequired": true }, @@ -2340,7 +2385,7 @@ }, "> | undefined" ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx", "deprecated": false, "isRequired": false } @@ -2397,7 +2442,7 @@ }, ">" ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx", "deprecated": false, "children": [ { @@ -2418,7 +2463,7 @@ }, ">" ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx", "deprecated": false, "isRequired": true }, @@ -2455,7 +2500,7 @@ }, "> | undefined" ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx", "deprecated": false, "isRequired": false } @@ -2474,7 +2519,7 @@ "DataControlField", ") => void" ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx", "deprecated": false, "children": [ { @@ -2487,73 +2532,13 @@ "signature": [ "DataControlField" ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx", "deprecated": false, "isRequired": true } ], "returnComment": [] }, - { - "parentPluginId": "controls", - "id": "def-public.RangeSliderEmbeddableFactory.isEditable", - "type": "Function", - "tags": [], - "label": "isEditable", - "description": [], - "signature": [ - "() => Promise" - ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "controls", - "id": "def-public.RangeSliderEmbeddableFactory.getDisplayName", - "type": "Function", - "tags": [], - "label": "getDisplayName", - "description": [], - "signature": [ - "() => string" - ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "controls", - "id": "def-public.RangeSliderEmbeddableFactory.getIconType", - "type": "Function", - "tags": [], - "label": "getIconType", - "description": [], - "signature": [ - "() => string" - ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "controls", - "id": "def-public.RangeSliderEmbeddableFactory.getDescription", - "type": "Function", - "tags": [], - "label": "getDescription", - "description": [], - "signature": [ - "() => string" - ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx", - "deprecated": false, - "children": [], - "returnComment": [] - }, { "parentPluginId": "controls", "id": "def-public.RangeSliderEmbeddableFactory.inject", @@ -2581,7 +2566,7 @@ "text": "EmbeddableStateWithType" } ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx", "deprecated": false, "returnComment": [], "children": [ @@ -2642,7 +2627,7 @@ "SavedObjectReference", "[]; }" ], - "path": "src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx", + "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx", "deprecated": false, "returnComment": [], "children": [ @@ -3281,7 +3266,7 @@ " extends ", "DataControlInput" ], - "path": "src/plugins/controls/common/control_types/range_slider/types.ts", + "path": "src/plugins/controls/common/range_slider/types.ts", "deprecated": false, "children": [ { @@ -3294,7 +3279,7 @@ "signature": [ "[string, string]" ], - "path": "src/plugins/controls/common/control_types/range_slider/types.ts", + "path": "src/plugins/controls/common/range_slider/types.ts", "deprecated": false } ], @@ -3529,7 +3514,7 @@ "signature": [ "\"rangeSliderControl\"" ], - "path": "src/plugins/controls/common/control_types/range_slider/types.ts", + "path": "src/plugins/controls/common/range_slider/types.ts", "deprecated": false, "initialIsOpen": false }, @@ -4263,7 +4248,7 @@ " extends ", "DataControlInput" ], - "path": "src/plugins/controls/common/control_types/range_slider/types.ts", + "path": "src/plugins/controls/common/range_slider/types.ts", "deprecated": false, "children": [ { @@ -4276,7 +4261,7 @@ "signature": [ "[string, string]" ], - "path": "src/plugins/controls/common/control_types/range_slider/types.ts", + "path": "src/plugins/controls/common/range_slider/types.ts", "deprecated": false } ], @@ -4395,7 +4380,7 @@ "signature": [ "\"rangeSliderControl\"" ], - "path": "src/plugins/controls/common/control_types/range_slider/types.ts", + "path": "src/plugins/controls/common/range_slider/types.ts", "deprecated": false, "initialIsOpen": false }, diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index 9e9162d8789ed..4f564cb0f1bc1 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-prese | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 207 | 0 | 199 | 7 | +| 206 | 0 | 198 | 7 | ## Client diff --git a/api_docs/core.devdocs.json b/api_docs/core.devdocs.json index a4b351471464c..416752a227c67 100644 --- a/api_docs/core.devdocs.json +++ b/api_docs/core.devdocs.json @@ -10498,9 +10498,1472 @@ } ], "initialIsOpen": false + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers", + "type": "Class", + "tags": [], + "label": "SavedObjectsErrorHelpers", + "description": [], + "signature": [ + "SavedObjectsErrorHelpers" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isSavedObjectsClientError", + "type": "Function", + "tags": [], + "label": "isSavedObjectsClientError", + "description": [], + "signature": [ + "(error: any) => error is ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isSavedObjectsClientError.$1", + "type": "Any", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "any" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateBadRequestError", + "type": "Function", + "tags": [], + "label": "decorateBadRequestError", + "description": [], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateBadRequestError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateBadRequestError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.createBadRequestError", + "type": "Function", + "tags": [], + "label": "createBadRequestError", + "description": [], + "signature": [ + "(reason?: string | undefined) => ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.createBadRequestError.$1", + "type": "string", + "tags": [], + "label": "reason", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.createUnsupportedTypeError", + "type": "Function", + "tags": [], + "label": "createUnsupportedTypeError", + "description": [], + "signature": [ + "(type: string) => ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.createUnsupportedTypeError.$1", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "string" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isBadRequestError", + "type": "Function", + "tags": [], + "label": "isBadRequestError", + "description": [], + "signature": [ + "(error: Error | ", + "DecoratedError", + ") => boolean" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isBadRequestError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error | ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.createInvalidVersionError", + "type": "Function", + "tags": [], + "label": "createInvalidVersionError", + "description": [], + "signature": [ + "(versionInput?: string | undefined) => ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.createInvalidVersionError.$1", + "type": "string", + "tags": [], + "label": "versionInput", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isInvalidVersionError", + "type": "Function", + "tags": [], + "label": "isInvalidVersionError", + "description": [], + "signature": [ + "(error: Error | ", + "DecoratedError", + ") => boolean" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isInvalidVersionError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error | ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateNotAuthorizedError", + "type": "Function", + "tags": [], + "label": "decorateNotAuthorizedError", + "description": [], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateNotAuthorizedError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateNotAuthorizedError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isNotAuthorizedError", + "type": "Function", + "tags": [], + "label": "isNotAuthorizedError", + "description": [], + "signature": [ + "(error: Error | ", + "DecoratedError", + ") => boolean" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isNotAuthorizedError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error | ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateForbiddenError", + "type": "Function", + "tags": [], + "label": "decorateForbiddenError", + "description": [], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateForbiddenError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateForbiddenError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isForbiddenError", + "type": "Function", + "tags": [], + "label": "isForbiddenError", + "description": [], + "signature": [ + "(error: Error | ", + "DecoratedError", + ") => boolean" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isForbiddenError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error | ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateRequestEntityTooLargeError", + "type": "Function", + "tags": [], + "label": "decorateRequestEntityTooLargeError", + "description": [], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateRequestEntityTooLargeError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateRequestEntityTooLargeError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isRequestEntityTooLargeError", + "type": "Function", + "tags": [], + "label": "isRequestEntityTooLargeError", + "description": [], + "signature": [ + "(error: Error | ", + "DecoratedError", + ") => boolean" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isRequestEntityTooLargeError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error | ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.createGenericNotFoundError", + "type": "Function", + "tags": [], + "label": "createGenericNotFoundError", + "description": [], + "signature": [ + "(type?: string | null | undefined, id?: string | null | undefined) => ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.createGenericNotFoundError.$1", + "type": "CompoundType", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "string | null | undefined" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": false + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.createGenericNotFoundError.$2", + "type": "CompoundType", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "string | null | undefined" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.createIndexAliasNotFoundError", + "type": "Function", + "tags": [], + "label": "createIndexAliasNotFoundError", + "description": [], + "signature": [ + "(alias: string) => ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.createIndexAliasNotFoundError.$1", + "type": "string", + "tags": [], + "label": "alias", + "description": [], + "signature": [ + "string" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateIndexAliasNotFoundError", + "type": "Function", + "tags": [], + "label": "decorateIndexAliasNotFoundError", + "description": [], + "signature": [ + "(error: Error, alias: string) => ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateIndexAliasNotFoundError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateIndexAliasNotFoundError.$2", + "type": "string", + "tags": [], + "label": "alias", + "description": [], + "signature": [ + "string" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isNotFoundError", + "type": "Function", + "tags": [], + "label": "isNotFoundError", + "description": [], + "signature": [ + "(error: Error | ", + "DecoratedError", + ") => boolean" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isNotFoundError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error | ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateConflictError", + "type": "Function", + "tags": [], + "label": "decorateConflictError", + "description": [], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateConflictError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateConflictError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.createConflictError", + "type": "Function", + "tags": [], + "label": "createConflictError", + "description": [], + "signature": [ + "(type: string, id: string, reason?: string | undefined) => ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.createConflictError.$1", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "string" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.createConflictError.$2", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "string" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.createConflictError.$3", + "type": "string", + "tags": [], + "label": "reason", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isConflictError", + "type": "Function", + "tags": [], + "label": "isConflictError", + "description": [], + "signature": [ + "(error: Error | ", + "DecoratedError", + ") => boolean" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isConflictError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error | ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateTooManyRequestsError", + "type": "Function", + "tags": [], + "label": "decorateTooManyRequestsError", + "description": [], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateTooManyRequestsError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateTooManyRequestsError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.createTooManyRequestsError", + "type": "Function", + "tags": [], + "label": "createTooManyRequestsError", + "description": [], + "signature": [ + "(type: string, id: string) => ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.createTooManyRequestsError.$1", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "string" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.createTooManyRequestsError.$2", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "string" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isTooManyRequestsError", + "type": "Function", + "tags": [], + "label": "isTooManyRequestsError", + "description": [], + "signature": [ + "(error: Error | ", + "DecoratedError", + ") => boolean" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isTooManyRequestsError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error | ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateEsCannotExecuteScriptError", + "type": "Function", + "tags": [], + "label": "decorateEsCannotExecuteScriptError", + "description": [], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateEsCannotExecuteScriptError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateEsCannotExecuteScriptError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isEsCannotExecuteScriptError", + "type": "Function", + "tags": [], + "label": "isEsCannotExecuteScriptError", + "description": [], + "signature": [ + "(error: Error | ", + "DecoratedError", + ") => boolean" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isEsCannotExecuteScriptError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error | ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateEsUnavailableError", + "type": "Function", + "tags": [], + "label": "decorateEsUnavailableError", + "description": [], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateEsUnavailableError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateEsUnavailableError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isEsUnavailableError", + "type": "Function", + "tags": [], + "label": "isEsUnavailableError", + "description": [], + "signature": [ + "(error: Error | ", + "DecoratedError", + ") => boolean" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isEsUnavailableError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error | ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateGeneralError", + "type": "Function", + "tags": [], + "label": "decorateGeneralError", + "description": [], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateGeneralError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.decorateGeneralError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isGeneralError", + "type": "Function", + "tags": [], + "label": "isGeneralError", + "description": [], + "signature": [ + "(error: Error | ", + "DecoratedError", + ") => boolean" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.isGeneralError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error | ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError", + "type": "Function", + "tags": [], + "label": "createGenericNotFoundEsUnavailableError", + "description": [], + "signature": [ + "(type?: string | null | undefined, id?: string | null | undefined) => ", + "DecoratedError" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError.$1", + "type": "CompoundType", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "string | null | undefined" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": false + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError.$2", + "type": "CompoundType", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "string | null | undefined" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsUtils", + "type": "Class", + "tags": [], + "label": "SavedObjectsUtils", + "description": [], + "signature": [ + "SavedObjectsUtils" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsUtils.namespaceIdToString", + "type": "Function", + "tags": [], + "label": "namespaceIdToString", + "description": [ + "\nConverts a given saved object namespace ID to its string representation. All namespace IDs have an identical string representation, with\nthe exception of the `undefined` namespace ID (which has a namespace string of `'default'`).\n" + ], + "signature": [ + "(namespace?: string | undefined) => string" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsUtils.namespaceIdToString.$1", + "type": "string", + "tags": [], + "label": "namespace", + "description": [ + "The namespace ID, which must be either a non-empty string or `undefined`." + ], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsUtils.namespaceStringToId", + "type": "Function", + "tags": [], + "label": "namespaceStringToId", + "description": [ + "\nConverts a given saved object namespace string to its ID representation. All namespace strings have an identical ID representation, with\nthe exception of the `'default'` namespace string (which has a namespace ID of `undefined`).\n" + ], + "signature": [ + "(namespace: string) => string | undefined" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsUtils.namespaceStringToId.$1", + "type": "string", + "tags": [], + "label": "namespace", + "description": [ + "The namespace string, which must be non-empty." + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsUtils.createEmptyFindResponse", + "type": "Function", + "tags": [], + "label": "createEmptyFindResponse", + "description": [ + "\nCreates an empty response for a find operation. This is only intended to be used by saved objects client wrappers." + ], + "signature": [ + "({ page, perPage, }: ", + "SavedObjectsFindOptions", + ") => ", + "SavedObjectsFindResponse", + "" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsUtils.createEmptyFindResponse.$1", + "type": "Object", + "tags": [], + "label": "__0", + "description": [], + "signature": [ + "SavedObjectsFindOptions" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsUtils.generateId", + "type": "Function", + "tags": [], + "label": "generateId", + "description": [ + "\nGenerates a random ID for a saved objects." + ], + "signature": [ + "() => string" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsUtils.isRandomId", + "type": "Function", + "tags": [ + "todo" + ], + "label": "isRandomId", + "description": [ + "\nValidates that a saved object ID has been randomly generated.\n" + ], + "signature": [ + "(id: string | undefined) => boolean" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsUtils.isRandomId.$1", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "The ID of a saved object." + ], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsUtils.getConvertedObjectId", + "type": "Function", + "tags": [], + "label": "getConvertedObjectId", + "description": [ + "\nUses a single-namespace object's \"legacy ID\" to determine what its new ID will be after it is converted to a multi-namespace type.\n" + ], + "signature": [ + "(namespace: string | undefined, type: string, id: string) => string" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsUtils.getConvertedObjectId.$1", + "type": "string", + "tags": [], + "label": "namespace", + "description": [ + "The namespace of the saved object before it is converted." + ], + "signature": [ + "string | undefined" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": false + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsUtils.getConvertedObjectId.$2", + "type": "string", + "tags": [], + "label": "type", + "description": [ + "The type of the saved object before it is converted." + ], + "signature": [ + "string" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "core", + "id": "def-server.SavedObjectsUtils.getConvertedObjectId.$3", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "The ID of the saved object before it is converted." + ], + "signature": [ + "string" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [ + "The ID of the saved object after it is converted." + ] + } + ], + "initialIsOpen": false } ], "functions": [ + { + "parentPluginId": "core", + "id": "def-server.mergeSavedObjectMigrationMaps", + "type": "Function", + "tags": [], + "label": "mergeSavedObjectMigrationMaps", + "description": [ + "\nMerges two saved object migration maps.\n\nIf there is a migration for a given version on only one of the maps,\nthat migration function will be used:\n\nmergeSavedObjectMigrationMaps({ '1.2.3': f }, { '4.5.6': g }) -> { '1.2.3': f, '4.5.6': g }\n\nIf there is a migration for a given version on both maps, the migrations will be composed:\n\nmergeSavedObjectMigrationMaps({ '1.2.3': f }, { '1.2.3': g }) -> { '1.2.3': (doc, context) => f(g(doc, context), context) }\n" + ], + "signature": [ + "(map1: ", + "SavedObjectMigrationMap", + ", map2: ", + "SavedObjectMigrationMap", + ") => ", + "SavedObjectMigrationMap" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "core", + "id": "def-server.mergeSavedObjectMigrationMaps.$1", + "type": "Object", + "tags": [], + "label": "map1", + "description": [], + "signature": [ + "SavedObjectMigrationMap" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false + }, + { + "parentPluginId": "core", + "id": "def-server.mergeSavedObjectMigrationMaps.$2", + "type": "Object", + "tags": [], + "label": "map2", + "description": [], + "signature": [ + "SavedObjectMigrationMap" + ], + "path": "node_modules/@types/kbn__core-saved-objects-utils-server/index.d.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "core", "id": "def-server.pollEsNodesVersion", diff --git a/api_docs/core.mdx b/api_docs/core.mdx index 3783f777f7e1e..33d87f244a33d 100644 --- a/api_docs/core.mdx +++ b/api_docs/core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/core title: "core" image: https://source.unsplash.com/400x175/?github description: API docs for the core plugin -date: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core'] --- import coreObj from './core.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2524 | 2 | 296 | 6 | +| 2524 | 1 | 216 | 5 | ## Client diff --git a/api_docs/core_application.mdx b/api_docs/core_application.mdx index d1254b7894002..cb769db54eaed 100644 --- a/api_docs/core_application.mdx +++ b/api_docs/core_application.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/core-application title: "core.application" image: https://source.unsplash.com/400x175/?github description: API docs for the core.application plugin -date: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core.application'] --- import coreApplicationObj from './core_application.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2524 | 2 | 296 | 6 | +| 2524 | 1 | 216 | 5 | ## Client diff --git a/api_docs/core_chrome.mdx b/api_docs/core_chrome.mdx index efa7993a1184b..bb6b32b4c04ea 100644 --- a/api_docs/core_chrome.mdx +++ b/api_docs/core_chrome.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/core-chrome title: "core.chrome" image: https://source.unsplash.com/400x175/?github description: API docs for the core.chrome plugin -date: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core.chrome'] --- import coreChromeObj from './core_chrome.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2524 | 2 | 296 | 6 | +| 2524 | 1 | 216 | 5 | ## Client diff --git a/api_docs/core_saved_objects.devdocs.json b/api_docs/core_saved_objects.devdocs.json index 7c494cf0fad8e..86cf67bdb6a5c 100644 --- a/api_docs/core_saved_objects.devdocs.json +++ b/api_docs/core_saved_objects.devdocs.json @@ -10,1181 +10,6 @@ }, "server": { "classes": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers", - "type": "Class", - "tags": [], - "label": "SavedObjectsErrorHelpers", - "description": [], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isSavedObjectsClientError", - "type": "Function", - "tags": [], - "label": "isSavedObjectsClientError", - "description": [], - "signature": [ - "(error: any) => error is ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isSavedObjectsClientError.$1", - "type": "Any", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "any" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateBadRequestError", - "type": "Function", - "tags": [], - "label": "decorateBadRequestError", - "description": [], - "signature": [ - "(error: Error, reason?: string | undefined) => ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateBadRequestError.$1", - "type": "Object", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateBadRequestError.$2", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.createBadRequestError", - "type": "Function", - "tags": [], - "label": "createBadRequestError", - "description": [], - "signature": [ - "(reason?: string | undefined) => ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.createBadRequestError.$1", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.createUnsupportedTypeError", - "type": "Function", - "tags": [], - "label": "createUnsupportedTypeError", - "description": [], - "signature": [ - "(type: string) => ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.createUnsupportedTypeError.$1", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "string" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isBadRequestError", - "type": "Function", - "tags": [], - "label": "isBadRequestError", - "description": [], - "signature": [ - "(error: Error | ", - "DecoratedError", - ") => boolean" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isBadRequestError.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error | ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.createInvalidVersionError", - "type": "Function", - "tags": [], - "label": "createInvalidVersionError", - "description": [], - "signature": [ - "(versionInput?: string | undefined) => ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.createInvalidVersionError.$1", - "type": "string", - "tags": [], - "label": "versionInput", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isInvalidVersionError", - "type": "Function", - "tags": [], - "label": "isInvalidVersionError", - "description": [], - "signature": [ - "(error: Error | ", - "DecoratedError", - ") => boolean" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isInvalidVersionError.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error | ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateNotAuthorizedError", - "type": "Function", - "tags": [], - "label": "decorateNotAuthorizedError", - "description": [], - "signature": [ - "(error: Error, reason?: string | undefined) => ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateNotAuthorizedError.$1", - "type": "Object", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateNotAuthorizedError.$2", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isNotAuthorizedError", - "type": "Function", - "tags": [], - "label": "isNotAuthorizedError", - "description": [], - "signature": [ - "(error: Error | ", - "DecoratedError", - ") => boolean" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isNotAuthorizedError.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error | ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateForbiddenError", - "type": "Function", - "tags": [], - "label": "decorateForbiddenError", - "description": [], - "signature": [ - "(error: Error, reason?: string | undefined) => ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateForbiddenError.$1", - "type": "Object", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateForbiddenError.$2", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isForbiddenError", - "type": "Function", - "tags": [], - "label": "isForbiddenError", - "description": [], - "signature": [ - "(error: Error | ", - "DecoratedError", - ") => boolean" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isForbiddenError.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error | ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateRequestEntityTooLargeError", - "type": "Function", - "tags": [], - "label": "decorateRequestEntityTooLargeError", - "description": [], - "signature": [ - "(error: Error, reason?: string | undefined) => ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateRequestEntityTooLargeError.$1", - "type": "Object", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateRequestEntityTooLargeError.$2", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isRequestEntityTooLargeError", - "type": "Function", - "tags": [], - "label": "isRequestEntityTooLargeError", - "description": [], - "signature": [ - "(error: Error | ", - "DecoratedError", - ") => boolean" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isRequestEntityTooLargeError.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error | ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.createGenericNotFoundError", - "type": "Function", - "tags": [], - "label": "createGenericNotFoundError", - "description": [], - "signature": [ - "(type?: string | null, id?: string | null) => ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.createGenericNotFoundError.$1", - "type": "CompoundType", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "string | null" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": false - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.createGenericNotFoundError.$2", - "type": "CompoundType", - "tags": [], - "label": "id", - "description": [], - "signature": [ - "string | null" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.createIndexAliasNotFoundError", - "type": "Function", - "tags": [], - "label": "createIndexAliasNotFoundError", - "description": [], - "signature": [ - "(alias: string) => ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.createIndexAliasNotFoundError.$1", - "type": "string", - "tags": [], - "label": "alias", - "description": [], - "signature": [ - "string" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateIndexAliasNotFoundError", - "type": "Function", - "tags": [], - "label": "decorateIndexAliasNotFoundError", - "description": [], - "signature": [ - "(error: Error, alias: string) => ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateIndexAliasNotFoundError.$1", - "type": "Object", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateIndexAliasNotFoundError.$2", - "type": "string", - "tags": [], - "label": "alias", - "description": [], - "signature": [ - "string" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isNotFoundError", - "type": "Function", - "tags": [], - "label": "isNotFoundError", - "description": [], - "signature": [ - "(error: Error | ", - "DecoratedError", - ") => boolean" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isNotFoundError.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error | ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateConflictError", - "type": "Function", - "tags": [], - "label": "decorateConflictError", - "description": [], - "signature": [ - "(error: Error, reason?: string | undefined) => ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateConflictError.$1", - "type": "Object", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateConflictError.$2", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.createConflictError", - "type": "Function", - "tags": [], - "label": "createConflictError", - "description": [], - "signature": [ - "(type: string, id: string, reason?: string | undefined) => ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.createConflictError.$1", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "string" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.createConflictError.$2", - "type": "string", - "tags": [], - "label": "id", - "description": [], - "signature": [ - "string" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.createConflictError.$3", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isConflictError", - "type": "Function", - "tags": [], - "label": "isConflictError", - "description": [], - "signature": [ - "(error: Error | ", - "DecoratedError", - ") => boolean" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isConflictError.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error | ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateTooManyRequestsError", - "type": "Function", - "tags": [], - "label": "decorateTooManyRequestsError", - "description": [], - "signature": [ - "(error: Error, reason?: string | undefined) => ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateTooManyRequestsError.$1", - "type": "Object", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateTooManyRequestsError.$2", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.createTooManyRequestsError", - "type": "Function", - "tags": [], - "label": "createTooManyRequestsError", - "description": [], - "signature": [ - "(type: string, id: string) => ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.createTooManyRequestsError.$1", - "type": "string", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "string" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.createTooManyRequestsError.$2", - "type": "string", - "tags": [], - "label": "id", - "description": [], - "signature": [ - "string" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isTooManyRequestsError", - "type": "Function", - "tags": [], - "label": "isTooManyRequestsError", - "description": [], - "signature": [ - "(error: Error | ", - "DecoratedError", - ") => boolean" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isTooManyRequestsError.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error | ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateEsCannotExecuteScriptError", - "type": "Function", - "tags": [], - "label": "decorateEsCannotExecuteScriptError", - "description": [], - "signature": [ - "(error: Error, reason?: string | undefined) => ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateEsCannotExecuteScriptError.$1", - "type": "Object", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateEsCannotExecuteScriptError.$2", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isEsCannotExecuteScriptError", - "type": "Function", - "tags": [], - "label": "isEsCannotExecuteScriptError", - "description": [], - "signature": [ - "(error: Error | ", - "DecoratedError", - ") => boolean" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isEsCannotExecuteScriptError.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error | ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateEsUnavailableError", - "type": "Function", - "tags": [], - "label": "decorateEsUnavailableError", - "description": [], - "signature": [ - "(error: Error, reason?: string | undefined) => ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateEsUnavailableError.$1", - "type": "Object", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateEsUnavailableError.$2", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isEsUnavailableError", - "type": "Function", - "tags": [], - "label": "isEsUnavailableError", - "description": [], - "signature": [ - "(error: Error | ", - "DecoratedError", - ") => boolean" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isEsUnavailableError.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error | ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateGeneralError", - "type": "Function", - "tags": [], - "label": "decorateGeneralError", - "description": [], - "signature": [ - "(error: Error, reason?: string | undefined) => ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateGeneralError.$1", - "type": "Object", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.decorateGeneralError.$2", - "type": "string", - "tags": [], - "label": "reason", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isGeneralError", - "type": "Function", - "tags": [], - "label": "isGeneralError", - "description": [], - "signature": [ - "(error: Error | ", - "DecoratedError", - ") => boolean" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.isGeneralError.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error | ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError", - "type": "Function", - "tags": [], - "label": "createGenericNotFoundEsUnavailableError", - "description": [], - "signature": [ - "(type?: string | null, id?: string | null) => ", - "DecoratedError" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError.$1", - "type": "CompoundType", - "tags": [], - "label": "type", - "description": [], - "signature": [ - "string | null" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": false - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError.$2", - "type": "CompoundType", - "tags": [], - "label": "id", - "description": [], - "signature": [ - "string | null" - ], - "path": "src/core/server/saved_objects/service/lib/errors.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] - } - ], - "initialIsOpen": false - }, { "parentPluginId": "core", "id": "def-server.SavedObjectsExportError", @@ -2791,300 +1616,9 @@ } ], "initialIsOpen": false - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsUtils", - "type": "Class", - "tags": [], - "label": "SavedObjectsUtils", - "description": [], - "path": "src/core/server/saved_objects/service/lib/utils.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsUtils.namespaceIdToString", - "type": "Function", - "tags": [], - "label": "namespaceIdToString", - "description": [ - "\nConverts a given saved object namespace ID to its string representation. All namespace IDs have an identical string representation, with\nthe exception of the `undefined` namespace ID (which has a namespace string of `'default'`).\n" - ], - "signature": [ - "(namespace?: string | undefined) => string" - ], - "path": "src/core/server/saved_objects/service/lib/utils.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsUtils.namespaceIdToString.$1", - "type": "string", - "tags": [], - "label": "namespace", - "description": [ - "The namespace ID, which must be either a non-empty string or `undefined`." - ], - "signature": [ - "string | undefined" - ], - "path": "src/core/server/saved_objects/service/lib/utils.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsUtils.namespaceStringToId", - "type": "Function", - "tags": [], - "label": "namespaceStringToId", - "description": [ - "\nConverts a given saved object namespace string to its ID representation. All namespace strings have an identical ID representation, with\nthe exception of the `'default'` namespace string (which has a namespace ID of `undefined`).\n" - ], - "signature": [ - "(namespace: string) => string | undefined" - ], - "path": "src/core/server/saved_objects/service/lib/utils.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsUtils.namespaceStringToId.$1", - "type": "string", - "tags": [], - "label": "namespace", - "description": [ - "The namespace string, which must be non-empty." - ], - "signature": [ - "string" - ], - "path": "src/core/server/saved_objects/service/lib/utils.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsUtils.createEmptyFindResponse", - "type": "Function", - "tags": [], - "label": "createEmptyFindResponse", - "description": [ - "\nCreates an empty response for a find operation. This is only intended to be used by saved objects client wrappers." - ], - "signature": [ - "({ page, perPage, }: ", - "SavedObjectsFindOptions", - ") => ", - "SavedObjectsFindResponse", - "" - ], - "path": "src/core/server/saved_objects/service/lib/utils.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsUtils.createEmptyFindResponse.$1", - "type": "Object", - "tags": [], - "label": "{\n page = FIND_DEFAULT_PAGE,\n perPage = FIND_DEFAULT_PER_PAGE,\n }", - "description": [], - "signature": [ - "SavedObjectsFindOptions" - ], - "path": "src/core/server/saved_objects/service/lib/utils.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsUtils.generateId", - "type": "Function", - "tags": [], - "label": "generateId", - "description": [ - "\nGenerates a random ID for a saved objects." - ], - "signature": [ - "() => string" - ], - "path": "src/core/server/saved_objects/service/lib/utils.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsUtils.isRandomId", - "type": "Function", - "tags": [ - "todo" - ], - "label": "isRandomId", - "description": [ - "\nValidates that a saved object ID has been randomly generated.\n" - ], - "signature": [ - "(id: string | undefined) => boolean" - ], - "path": "src/core/server/saved_objects/service/lib/utils.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsUtils.isRandomId.$1", - "type": "string", - "tags": [], - "label": "id", - "description": [ - "The ID of a saved object." - ], - "signature": [ - "string | undefined" - ], - "path": "src/core/server/saved_objects/service/lib/utils.ts", - "deprecated": false, - "isRequired": false - } - ], - "returnComment": [] - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsUtils.getConvertedObjectId", - "type": "Function", - "tags": [], - "label": "getConvertedObjectId", - "description": [ - "\nUses a single-namespace object's \"legacy ID\" to determine what its new ID will be after it is converted to a multi-namespace type.\n" - ], - "signature": [ - "(namespace: string | undefined, type: string, id: string) => string" - ], - "path": "src/core/server/saved_objects/service/lib/utils.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsUtils.getConvertedObjectId.$1", - "type": "string", - "tags": [], - "label": "namespace", - "description": [ - "The namespace of the saved object before it is converted." - ], - "signature": [ - "string | undefined" - ], - "path": "src/core/server/saved_objects/service/lib/utils.ts", - "deprecated": false, - "isRequired": false - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsUtils.getConvertedObjectId.$2", - "type": "string", - "tags": [], - "label": "type", - "description": [ - "The type of the saved object before it is converted." - ], - "signature": [ - "string" - ], - "path": "src/core/server/saved_objects/service/lib/utils.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "core", - "id": "def-server.SavedObjectsUtils.getConvertedObjectId.$3", - "type": "string", - "tags": [], - "label": "id", - "description": [ - "The ID of the saved object before it is converted." - ], - "signature": [ - "string" - ], - "path": "src/core/server/saved_objects/service/lib/utils.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [ - "The ID of the saved object after it is converted." - ] - } - ], - "initialIsOpen": false - } - ], - "functions": [ - { - "parentPluginId": "core", - "id": "def-server.mergeSavedObjectMigrationMaps", - "type": "Function", - "tags": [], - "label": "mergeSavedObjectMigrationMaps", - "description": [ - "\nMerges two saved object migration maps.\n\nIf there is a migration for a given version on only one of the maps,\nthat migration function will be used:\n\nmergeSavedObjectMigrationMaps({ '1.2.3': f }, { '4.5.6': g }) -> { '1.2.3': f, '4.5.6': g }\n\nIf there is a migration for a given version on both maps, the migrations will be composed:\n\nmergeSavedObjectMigrationMaps({ '1.2.3': f }, { '1.2.3': g }) -> { '1.2.3': (doc, context) => f(g(doc, context), context) }\n" - ], - "signature": [ - "(map1: ", - "SavedObjectMigrationMap", - ", map2: ", - "SavedObjectMigrationMap", - ") => ", - "SavedObjectMigrationMap" - ], - "path": "src/core/server/saved_objects/migrations/utils.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-server.mergeSavedObjectMigrationMaps.$1", - "type": "Object", - "tags": [], - "label": "map1", - "description": [], - "signature": [ - "SavedObjectMigrationMap" - ], - "path": "src/core/server/saved_objects/migrations/utils.ts", - "deprecated": false, - "isRequired": true - }, - { - "parentPluginId": "core", - "id": "def-server.mergeSavedObjectMigrationMaps.$2", - "type": "Object", - "tags": [], - "label": "map2", - "description": [], - "signature": [ - "SavedObjectMigrationMap" - ], - "path": "src/core/server/saved_objects/migrations/utils.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false } ], + "functions": [], "interfaces": [], "enums": [], "misc": [], diff --git a/api_docs/core_saved_objects.mdx b/api_docs/core_saved_objects.mdx index afad4fbff7034..684e4f0036a6f 100644 --- a/api_docs/core_saved_objects.mdx +++ b/api_docs/core_saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/core-savedObjects title: "core.savedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the core.savedObjects plugin -date: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core.savedObjects'] --- import coreSavedObjectsObj from './core_saved_objects.devdocs.json'; @@ -21,13 +21,10 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2524 | 2 | 296 | 6 | +| 2524 | 1 | 216 | 5 | ## Server -### Functions - - ### Classes diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index ed599bcb6ba83..321197a449a9d 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: 2022-08-19 +date: 2022-08-23 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 ece6f1ad6a329..682aad767ec99 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: 2022-08-19 +date: 2022-08-23 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 4bc1b3cc322ab..b6181b13a58e6 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.devdocs.json b/api_docs/data.devdocs.json index 78c8f4c0dec76..92bb53862bd10 100644 --- a/api_docs/data.devdocs.json +++ b/api_docs/data.devdocs.json @@ -10634,6 +10634,42 @@ ], "path": "src/plugins/data/public/index.ts", "deprecated": false + }, + { + "parentPluginId": "data", + "id": "def-public.search.aggs.calcAutoIntervalLessThan", + "type": "Function", + "tags": [], + "label": "calcAutoIntervalLessThan", + "description": [], + "signature": [ + "(maxBucketCount: number, duration: number) => moment.Duration" + ], + "path": "src/plugins/data/public/index.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "data", + "id": "def-public.search.aggs.calcAutoIntervalLessThan.$1", + "type": "number", + "tags": [], + "label": "maxBucketCount", + "description": [], + "path": "src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_auto_interval.ts", + "deprecated": false + }, + { + "parentPluginId": "data", + "id": "def-public.search.aggs.calcAutoIntervalLessThan.$2", + "type": "number", + "tags": [], + "label": "duration", + "description": [], + "path": "src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_auto_interval.ts", + "deprecated": false + } + ] } ] }, diff --git a/api_docs/data.mdx b/api_docs/data.mdx index c6549285f3b8b..a4dfc956ab86a 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; @@ -21,7 +21,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3114 | 34 | 2428 | 22 | +| 3117 | 34 | 2431 | 22 | ## Client diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index 194b2bd364e45..bff320e0afb5b 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; @@ -21,7 +21,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3114 | 34 | 2428 | 22 | +| 3117 | 34 | 2431 | 22 | ## Client diff --git a/api_docs/data_search.devdocs.json b/api_docs/data_search.devdocs.json index 73d0c6793eac8..6d60b92265a0f 100644 --- a/api_docs/data_search.devdocs.json +++ b/api_docs/data_search.devdocs.json @@ -20481,7 +20481,7 @@ "label": "aggregate", "description": [], "signature": [ - "\"min\" | \"max\" | \"concat\" | \"sum\" | \"average\"" + "\"min\" | \"max\" | \"sum\" | \"average\" | \"concat\"" ], "path": "src/plugins/data/common/search/aggs/metrics/top_hit.ts", "deprecated": false diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 297dcc48c5ef2..4f867c11de78b 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; @@ -21,7 +21,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3114 | 34 | 2428 | 22 | +| 3117 | 34 | 2431 | 22 | ## Client diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 36cd599972d3a..072af10cfd44f 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: 2022-08-19 +date: 2022-08-23 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 9153a73811501..da4c7c83a4f49 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: 2022-08-19 +date: 2022-08-23 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 4581db94f2259..0058eed3fbd91 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: 2022-08-19 +date: 2022-08-23 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 f91c548c7fe83..ddd52b1c6887d 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: 2022-08-19 +date: 2022-08-23 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 f13ad6b138d7b..c013d84288b44 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: 2022-08-19 +date: 2022-08-23 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 c05408c6c8cb9..289fec2eb63cf 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 63b1da440c062..cc570a401d482 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 351da70381379..7857bea1ccfba 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 7717ef5fa7e9f..47e2425b3b89d 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: 2022-08-19 +date: 2022-08-23 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 85691c4be0962..e9516c80dd08e 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: 2022-08-19 +date: 2022-08-23 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 056047fc72edf..89e46db501521 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 3679c9e256649..c1573e9c557fe 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: 2022-08-19 +date: 2022-08-23 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 7c480eb500091..5d55592350d12 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: 2022-08-19 +date: 2022-08-23 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 4be7c6b84d414..bca90471f04d4 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: 2022-08-19 +date: 2022-08-23 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 ad904ff6c0cd6..e15f217b3caf0 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: 2022-08-19 +date: 2022-08-23 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 25ddf1aa42d04..76043ca99d8af 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: 2022-08-19 +date: 2022-08-23 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 4d54ae80af584..32551880db4f7 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: 2022-08-19 +date: 2022-08-23 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 35b2a73c2d732..f98a5c6b57bdc 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index f5dd4d7842c7e..86c6a90aad327 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: 2022-08-19 +date: 2022-08-23 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 389703edd9ca4..770f43f403edf 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: 2022-08-19 +date: 2022-08-23 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 87c9cba997837..1495b6a3b7b17 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: 2022-08-19 +date: 2022-08-23 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 fa783631b3666..c86d8e4cd39e0 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: 2022-08-19 +date: 2022-08-23 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 1580a9b512e7e..434a95d560c9b 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: 2022-08-19 +date: 2022-08-23 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 5af5a8eb25b00..def0586f94784 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: 2022-08-19 +date: 2022-08-23 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 c7e5b092d900b..d735e0eac87ee 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: 2022-08-19 +date: 2022-08-23 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 c8040b4f7ae3c..e6661944b12d3 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: 2022-08-19 +date: 2022-08-23 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 2f5d3ab0a548f..c4d497fb88fb6 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: 2022-08-19 +date: 2022-08-23 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 7caa777963f6c..98afa2b7e6eef 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: 2022-08-19 +date: 2022-08-23 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 0ceaf000e47ba..ebaab4968e7d9 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: 2022-08-19 +date: 2022-08-23 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 ed7104d1c75a6..2bf997203e91b 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: 2022-08-19 +date: 2022-08-23 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 e586f1b0c68e6..620dfb19c8824 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: 2022-08-19 +date: 2022-08-23 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 39b9017801b60..18f6377fbf287 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: 2022-08-19 +date: 2022-08-23 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 18e402c2bf154..45d9c4d70c28d 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: 2022-08-19 +date: 2022-08-23 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 88f8daf4b5113..9a6de84523cdc 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: 2022-08-19 +date: 2022-08-23 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 2c109c2d1b3a8..79e16545d5f05 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: 2022-08-19 +date: 2022-08-23 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 ad95a0fb51c03..ccf5fc8ac5560 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index af00182919a87..3119de26cc49e 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: 2022-08-19 +date: 2022-08-23 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 3f355ba9ab281..f30a92d9dc6c8 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] --- import globalSearchObj from './global_search.devdocs.json'; diff --git a/api_docs/home.mdx b/api_docs/home.mdx index 0557452b4083a..b5e237a77869c 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] --- import homeObj from './home.devdocs.json'; diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index 2f03debcd3012..3079b15c14c35 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: 2022-08-19 +date: 2022-08-23 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 19043c1604c59..2887cdcbf2bca 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: 2022-08-19 +date: 2022-08-23 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 6fd2b65447b72..5c31619bbe540 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: 2022-08-19 +date: 2022-08-23 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 66d3de6650306..62ae1f5710475 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: 2022-08-19 +date: 2022-08-23 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 0755f23730c46..88049ba3d455f 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: 2022-08-19 +date: 2022-08-23 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 150518da0cae8..54f8426838e8f 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: 2022-08-19 +date: 2022-08-23 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 798716a926a51..8bdbc985b6bc5 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: 2022-08-19 +date: 2022-08-23 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 3876efca7e1cd..d220922d8e8e1 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] --- import kbnAiopsUtilsObj from './kbn_aiops_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts.mdx b/api_docs/kbn_alerts.mdx index da8e828b03869..3b006d777b2f5 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts'] --- import kbnAlertsObj from './kbn_alerts.devdocs.json'; diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index 622d488cc6163..07a2b081aa682 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: 2022-08-19 +date: 2022-08-23 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 c660b8eaa946a..10cba6c9c818a 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: 2022-08-19 +date: 2022-08-23 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 d7eaaa4339fb1..dd212d82d5456 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: 2022-08-19 +date: 2022-08-23 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 3b9b809407c40..caa5e741760be 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: 2022-08-19 +date: 2022-08-23 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 be81f0cd944df..e9c60a3a541bd 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: 2022-08-19 +date: 2022-08-23 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 bb1ad790890aa..44d9002a74c02 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 5b75866be4093..3c912520647ec 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: 2022-08-19 +date: 2022-08-23 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 e5bb0636fba65..7f3da3e562afc 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index 1c5771f4c647d..fb47443b3f652 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: 2022-08-19 +date: 2022-08-23 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 f696abe1eb1f7..06c3b25d6e655 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_bazel_packages.mdx b/api_docs/kbn_bazel_packages.mdx index c526cdc6bba28..12d7d9c3cf81b 100644 --- a/api_docs/kbn_bazel_packages.mdx +++ b/api_docs/kbn_bazel_packages.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-bazel-packages title: "@kbn/bazel-packages" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/bazel-packages plugin -date: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/bazel-packages'] --- import kbnBazelPackagesObj from './kbn_bazel_packages.devdocs.json'; diff --git a/api_docs/kbn_chart_icons.mdx b/api_docs/kbn_chart_icons.mdx index cfc7386073327..a96c307eaa5ef 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: 2022-08-19 +date: 2022-08-23 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 53ba6affac22f..8cc7a94eb87f9 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: 2022-08-19 +date: 2022-08-23 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 dcac8be979876..9dfac087b5691 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: 2022-08-19 +date: 2022-08-23 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 c895dd56b43ae..ffb861a5ed784 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: 2022-08-19 +date: 2022-08-23 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 5612154d5f963..d6e2e8be716b3 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index 3e1f46dc7397d..a173a60760e16 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: 2022-08-19 +date: 2022-08-23 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 2c9396746d6f0..3bfaefa7da26e 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: 2022-08-19 +date: 2022-08-23 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 cfa5aed98b295..9940d262ed90b 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: 2022-08-19 +date: 2022-08-23 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 548c3032fe528..673b933cd455c 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index ce4869d31cc51..0adf25037c19c 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: 2022-08-19 +date: 2022-08-23 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 e3ef1e80f3bc6..5cb22be772a3e 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: 2022-08-19 +date: 2022-08-23 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 1f0dde355ba0f..4512695964200 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: 2022-08-19 +date: 2022-08-23 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 525c933ebe5b5..c844026ed5d9d 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: 2022-08-19 +date: 2022-08-23 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 450a0599d105a..900c76a8b5082 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: 2022-08-19 +date: 2022-08-23 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 bf5019da05258..9b5006e1f40c2 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: 2022-08-19 +date: 2022-08-23 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_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index 7f778d200ed42..3041bc1651ab4 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: 2022-08-19 +date: 2022-08-23 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 cfa19531cec60..1b7b3fcf69dd7 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: 2022-08-19 +date: 2022-08-23 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 9cad996d5d1ac..b2add4d4a1978 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: 2022-08-19 +date: 2022-08-23 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 7e81990f1064c..70ede2118a655 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: 2022-08-19 +date: 2022-08-23 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_common.mdx b/api_docs/kbn_core_capabilities_common.mdx index ed4ff93770ebe..b49e0e3c64fe6 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: 2022-08-19 +date: 2022-08-23 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 76c90f13f18d1..4d752a0370c6c 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: 2022-08-19 +date: 2022-08-23 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 5f6270d814d4c..95c7bb7c2a941 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: 2022-08-19 +date: 2022-08-23 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_config_server_internal.mdx b/api_docs/kbn_core_config_server_internal.mdx index f55e122d9f164..66a5642f9fa36 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: 2022-08-19 +date: 2022-08-23 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_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index 7f567d9888d57..34185f64d10ff 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: 2022-08-19 +date: 2022-08-23 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 9b53cd4fe5148..2b75a68053f77 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: 2022-08-19 +date: 2022-08-23 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 78580fdcb729e..6a3748703e4f3 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: 2022-08-19 +date: 2022-08-23 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 bbf55bba903af..88024b0421ddd 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: 2022-08-19 +date: 2022-08-23 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_doc_links_browser.mdx b/api_docs/kbn_core_doc_links_browser.mdx index f5c7d31eb3fc0..c48481e85fab4 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: 2022-08-19 +date: 2022-08-23 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 bea243e2ef8a1..433ec8c2418dc 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: 2022-08-19 +date: 2022-08-23 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 72a53a4bf97d6..c76b4b1683ea1 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: 2022-08-19 +date: 2022-08-23 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 e820fa2abc9b0..3a6ffa6671de2 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: 2022-08-19 +date: 2022-08-23 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 36f746569672d..e79bf5cfd43ab 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: 2022-08-19 +date: 2022-08-23 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 7e33a2d7c4bf6..3c90cd43f3277 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: 2022-08-19 +date: 2022-08-23 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 409a6a48a6bb3..974ff5f772047 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: 2022-08-19 +date: 2022-08-23 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 73bd16784e135..37dbb74ebf9cc 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: 2022-08-19 +date: 2022-08-23 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 d8413a779219f..bd6c5e057ee3b 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: 2022-08-19 +date: 2022-08-23 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 951234efbdea8..d8cf266601451 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: 2022-08-19 +date: 2022-08-23 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 68e2043bc8916..654d2649b7feb 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: 2022-08-19 +date: 2022-08-23 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 820692b0d547e..6e8fe83b89531 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: 2022-08-19 +date: 2022-08-23 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 053ed3c0596f5..c92347071b4b0 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: 2022-08-19 +date: 2022-08-23 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 4bdff37922464..cfffe837864f0 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: 2022-08-19 +date: 2022-08-23 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 9ca689e474dc9..96c76f0663414 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: 2022-08-19 +date: 2022-08-23 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 1f2cd09af5546..fe641b402acf8 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: 2022-08-19 +date: 2022-08-23 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 d34cf6ab43e2c..29a9eab7a276a 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: 2022-08-19 +date: 2022-08-23 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 56ae34a6f8f1c..59c6e46e4168b 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: 2022-08-19 +date: 2022-08-23 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 82526d4048175..d82654e942eb3 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: 2022-08-19 +date: 2022-08-23 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 33a68ea888b35..075458fbfe269 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: 2022-08-19 +date: 2022-08-23 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 33e004745ff27..418ae645fa1a3 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: 2022-08-19 +date: 2022-08-23 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 873f360196bdc..f83ba3ae57980 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: 2022-08-19 +date: 2022-08-23 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 395946658f586..c11524f8d61fc 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: 2022-08-19 +date: 2022-08-23 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 aaf5d5d9880b4..9d68d1b05cd0a 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: 2022-08-19 +date: 2022-08-23 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 0a6fff1c0ff34..e71dc5f1dd2ac 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: 2022-08-19 +date: 2022-08-23 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_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index b5a392ae7698f..5bc43d071fea6 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: 2022-08-19 +date: 2022-08-23 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 30d9f569a74d4..74d081091554f 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: 2022-08-19 +date: 2022-08-23 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 5662a7f851eb1..4f4323720fdb6 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: 2022-08-19 +date: 2022-08-23 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 bfb328c5e820c..08d45cbc7cd69 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: 2022-08-19 +date: 2022-08-23 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 23ba53c31694c..14a503b3201b8 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: 2022-08-19 +date: 2022-08-23 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 94599d250aad9..d0fd9e4b5c667 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: 2022-08-19 +date: 2022-08-23 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 d55fe68892d25..2816d317cf9df 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: 2022-08-19 +date: 2022-08-23 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_injected_metadata_browser.mdx b/api_docs/kbn_core_injected_metadata_browser.mdx index aa56fa4a695bf..1308d664c1da3 100644 --- a/api_docs/kbn_core_injected_metadata_browser.mdx +++ b/api_docs/kbn_core_injected_metadata_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser title: "@kbn/core-injected-metadata-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser plugin -date: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser'] --- import kbnCoreInjectedMetadataBrowserObj from './kbn_core_injected_metadata_browser.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 0358ec858c62b..a4c9dd46cf83a 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: 2022-08-19 +date: 2022-08-23 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 d272f6aa01978..e0bd552efad25 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: 2022-08-19 +date: 2022-08-23 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 fe2bf86133a68..9a3622e48e5ab 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: 2022-08-19 +date: 2022-08-23 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_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index c981831aa7414..e2f257a283f5e 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: 2022-08-19 +date: 2022-08-23 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 7ff970c967a6f..7a684b587e50e 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: 2022-08-19 +date: 2022-08-23 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 8baff40732b80..f4598190bd6b1 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: 2022-08-19 +date: 2022-08-23 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 eccdd22ced041..32a1e8edb9e49 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: 2022-08-19 +date: 2022-08-23 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 d1a63a529fdc9..bef75fc492a0a 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: 2022-08-19 +date: 2022-08-23 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 fa35f14fcf725..bc75927c87c65 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: 2022-08-19 +date: 2022-08-23 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 b2e404325e65c..983b863b4cffa 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: 2022-08-19 +date: 2022-08-23 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 c69f1ca4c566b..a44699b9829af 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: 2022-08-19 +date: 2022-08-23 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 9a39b9498f78e..6c4c988800920 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: 2022-08-19 +date: 2022-08-23 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_mount_utils_browser_internal.mdx b/api_docs/kbn_core_mount_utils_browser_internal.mdx index 24f044df27729..bbe50b7afe127 100644 --- a/api_docs/kbn_core_mount_utils_browser_internal.mdx +++ b/api_docs/kbn_core_mount_utils_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser-internal title: "@kbn/core-mount-utils-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-mount-utils-browser-internal plugin -date: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-mount-utils-browser-internal'] --- import kbnCoreMountUtilsBrowserInternalObj from './kbn_core_mount_utils_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_node_server.mdx b/api_docs/kbn_core_node_server.mdx index 762f5c1126971..b4df77f64ea41 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: 2022-08-19 +date: 2022-08-23 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 2a0eacf290842..161f626fb4eb1 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: 2022-08-19 +date: 2022-08-23 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 0c820deb6aa3b..502fd822054a4 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: 2022-08-19 +date: 2022-08-23 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 e2ba35b35ce89..16e7af167a3a0 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: 2022-08-19 +date: 2022-08-23 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 cf192a38d19db..34918eeecc9cd 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: 2022-08-19 +date: 2022-08-23 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 80785605085cf..08d6ab0c9ddf9 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: 2022-08-19 +date: 2022-08-23 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 1a8acdc05a552..6043dc1a389af 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: 2022-08-19 +date: 2022-08-23 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 9ee9f9d251e96..f4e12809cc9ce 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: 2022-08-19 +date: 2022-08-23 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 518924f7ddfd3..2f2b7d0034526 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: 2022-08-19 +date: 2022-08-23 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_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index 5df93a73c822a..b719c8b63a3ed 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: 2022-08-19 +date: 2022-08-23 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 43f20cac4663f..b77516104ef8f 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: 2022-08-19 +date: 2022-08-23 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_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index 9e6e6cd72e4e3..831cd004a8b31 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: 2022-08-19 +date: 2022-08-23 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 9e2a13abe4aa4..40138d2729502 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: 2022-08-19 +date: 2022-08-23 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_base_server_internal.devdocs.json b/api_docs/kbn_core_saved_objects_base_server_internal.devdocs.json new file mode 100644 index 0000000000000..b30cc8fc4006f --- /dev/null +++ b/api_docs/kbn_core_saved_objects_base_server_internal.devdocs.json @@ -0,0 +1,585 @@ +{ + "id": "@kbn/core-saved-objects-base-server-internal", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.SavedObjectConfig", + "type": "Class", + "tags": [], + "label": "SavedObjectConfig", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_config.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.SavedObjectConfig.maxImportPayloadBytes", + "type": "number", + "tags": [], + "label": "maxImportPayloadBytes", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_config.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.SavedObjectConfig.maxImportExportSize", + "type": "number", + "tags": [], + "label": "maxImportExportSize", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_config.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.SavedObjectConfig.migration", + "type": "Object", + "tags": [], + "label": "migration", + "description": [], + "signature": [ + "{ readonly discardUnknownObjects?: string | undefined; readonly discardCorruptObjects?: string | undefined; readonly pollInterval: number; readonly skip: boolean; readonly batchSize: number; readonly maxBatchSizeBytes: ", + "ByteSizeValue", + "; readonly scrollDuration: string; readonly retryAttempts: number; }" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_config.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.SavedObjectConfig.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_config.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.SavedObjectConfig.Unnamed.$1", + "type": "Object", + "tags": [], + "label": "rawConfig", + "description": [], + "signature": [ + "Readonly<{} & { maxImportPayloadBytes: ", + "ByteSizeValue", + "; maxImportExportSize: number; }>" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_config.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.SavedObjectConfig.Unnamed.$2", + "type": "Object", + "tags": [], + "label": "rawMigrationConfig", + "description": [], + "signature": [ + "Readonly<{ discardUnknownObjects?: string | undefined; discardCorruptObjects?: string | undefined; } & { pollInterval: number; skip: boolean; batchSize: number; maxBatchSizeBytes: ", + "ByteSizeValue", + "; scrollDuration: string; retryAttempts: number; }>" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_config.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "functions": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.decodeRequestVersion", + "type": "Function", + "tags": [], + "label": "decodeRequestVersion", + "description": [ + "\nHelper for decoding version to request params that are driven\nby the version info" + ], + "signature": [ + "(version: string | undefined) => { if_seq_no: number; if_primary_term: number; }" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/decode_request_version.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.decodeRequestVersion.$1", + "type": "string", + "tags": [], + "label": "version", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/decode_request_version.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.decodeVersion", + "type": "Function", + "tags": [], + "label": "decodeVersion", + "description": [ + "\nDecode the \"opaque\" version string to the sequence params we\ncan use to activate optimistic concurrency in Elasticsearch" + ], + "signature": [ + "(version: string | undefined) => { _seq_no: number; _primary_term: number; }" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/decode_version.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.decodeVersion.$1", + "type": "string", + "tags": [], + "label": "version", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/decode_version.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.encodeHitVersion", + "type": "Function", + "tags": [], + "label": "encodeHitVersion", + "description": [ + "\nHelper for encoding a version from a \"hit\" (hits.hits[#] from _search) or\n\"doc\" (body from GET, update, etc) object" + ], + "signature": [ + "(response: { _seq_no?: number | undefined; _primary_term?: number | undefined; }) => string" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/encode_hit_version.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.encodeHitVersion.$1", + "type": "Object", + "tags": [], + "label": "response", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/encode_hit_version.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.encodeHitVersion.$1._seq_no", + "type": "number", + "tags": [], + "label": "_seq_no", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/encode_hit_version.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.encodeHitVersion.$1._primary_term", + "type": "number", + "tags": [], + "label": "_primary_term", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/encode_hit_version.ts", + "deprecated": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.encodeVersion", + "type": "Function", + "tags": [], + "label": "encodeVersion", + "description": [ + "\nEncode the sequence params into an \"opaque\" version string\nthat can be used in the saved object API in place of numeric\nversion numbers" + ], + "signature": [ + "(seqNo: number | undefined, primaryTerm: number | undefined) => string" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/encode_version.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.encodeVersion.$1", + "type": "number", + "tags": [], + "label": "seqNo", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/encode_version.ts", + "deprecated": false, + "isRequired": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.encodeVersion.$2", + "type": "number", + "tags": [], + "label": "primaryTerm", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/encode_version.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.getProperty", + "type": "Function", + "tags": [], + "label": "getProperty", + "description": [], + "signature": [ + "(mappings: ", + "SavedObjectsFieldMapping", + " | ", + "IndexMapping", + ", path: string | string[]) => ", + "SavedObjectsFieldMapping", + " | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/lib/get_property.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.getProperty.$1", + "type": "CompoundType", + "tags": [], + "label": "mappings", + "description": [], + "signature": [ + "SavedObjectsFieldMapping", + " | ", + "IndexMapping" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/lib/get_property.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.getProperty.$2", + "type": "CompoundType", + "tags": [], + "label": "path", + "description": [], + "signature": [ + "string | string[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/lib/get_property.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.getRootProperties", + "type": "Function", + "tags": [], + "label": "getRootProperties", + "description": [ + "\n Get the property mappings for the root type in the EsMappingsDsl\n\n If the mappings don't have a root type, or the root type is not\n an object type (it's a keyword or something) this function will\n throw an error.\n\n EsPropertyMappings objects have the root property names as their\n first level keys which map to the mappings object for each property.\n If the property is of type object it too could have a `properties`\n key whose value follows the same format.\n\n This data can be found at `{indexName}.mappings.{typeName}.properties`\n in the es indices.get() response." + ], + "signature": [ + "(mapping: ", + "IndexMapping", + ") => ", + "SavedObjectsMappingProperties" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/lib/get_root_properties.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.getRootProperties.$1", + "type": "Object", + "tags": [], + "label": "mapping", + "description": [], + "signature": [ + "IndexMapping" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/lib/get_root_properties.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.getRootPropertiesObjects", + "type": "Function", + "tags": [], + "label": "getRootPropertiesObjects", + "description": [], + "signature": [ + "(mappings: ", + "IndexMapping", + ") => ", + "SavedObjectsMappingProperties" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/lib/get_root_properties_objects.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.getRootPropertiesObjects.$1", + "type": "Object", + "tags": [], + "label": "mappings", + "description": [], + "signature": [ + "IndexMapping" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/lib/get_root_properties_objects.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.getTypes", + "type": "Function", + "tags": [], + "label": "getTypes", + "description": [ + "\n Get the names of the types defined in the EsMappingsDsl" + ], + "signature": [ + "(mappings: ", + "IndexMapping", + ") => string[]" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/lib/get_types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.getTypes.$1", + "type": "Object", + "tags": [], + "label": "mappings", + "description": [], + "signature": [ + "IndexMapping" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/lib/get_types.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.SavedObjectsConfigType", + "type": "Type", + "tags": [], + "label": "SavedObjectsConfigType", + "description": [], + "signature": [ + "{ readonly maxImportPayloadBytes: ", + "ByteSizeValue", + "; readonly maxImportExportSize: number; }" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_config.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.SavedObjectsMigrationConfigType", + "type": "Type", + "tags": [], + "label": "SavedObjectsMigrationConfigType", + "description": [], + "signature": [ + "{ readonly discardUnknownObjects?: string | undefined; readonly discardCorruptObjects?: string | undefined; readonly pollInterval: number; readonly skip: boolean; readonly batchSize: number; readonly maxBatchSizeBytes: ", + "ByteSizeValue", + "; readonly scrollDuration: string; readonly retryAttempts: number; }" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_config.ts", + "deprecated": false, + "initialIsOpen": false + } + ], + "objects": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.savedObjectsConfig", + "type": "Object", + "tags": [], + "label": "savedObjectsConfig", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_config.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.savedObjectsConfig.path", + "type": "string", + "tags": [], + "label": "path", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_config.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.savedObjectsConfig.schema", + "type": "Object", + "tags": [], + "label": "schema", + "description": [], + "signature": [ + "ObjectType", + "<{ maxImportPayloadBytes: ", + "Type", + "<", + "ByteSizeValue", + ">; maxImportExportSize: ", + "Type", + "; }>" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_config.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.savedObjectsMigrationConfig", + "type": "Object", + "tags": [], + "label": "savedObjectsMigrationConfig", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_config.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.savedObjectsMigrationConfig.path", + "type": "string", + "tags": [], + "label": "path", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_config.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-internal", + "id": "def-server.savedObjectsMigrationConfig.schema", + "type": "Object", + "tags": [], + "label": "schema", + "description": [], + "signature": [ + "ObjectType", + "<{ batchSize: ", + "Type", + "; maxBatchSizeBytes: ", + "Type", + "<", + "ByteSizeValue", + ">; discardUnknownObjects: ", + "Type", + "; discardCorruptObjects: ", + "Type", + "; scrollDuration: ", + "Type", + "; pollInterval: ", + "Type", + "; skip: ", + "Type", + "; retryAttempts: ", + "Type", + "; }>" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_config.ts", + "deprecated": false + } + ], + "initialIsOpen": false + } + ] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_core_saved_objects_base_server_internal.mdx b/api_docs/kbn_core_saved_objects_base_server_internal.mdx new file mode 100644 index 0000000000000..9db42c2419566 --- /dev/null +++ b/api_docs/kbn_core_saved_objects_base_server_internal.mdx @@ -0,0 +1,39 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnCoreSavedObjectsBaseServerInternalPluginApi +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: 2022-08-23 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-internal'] +--- +import kbnCoreSavedObjectsBaseServerInternalObj from './kbn_core_saved_objects_base_server_internal.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 35 | 0 | 29 | 1 | + +## Server + +### Objects + + +### Functions + + +### Classes + + +### Consts, variables and types + + diff --git a/api_docs/kbn_core_saved_objects_base_server_mocks.devdocs.json b/api_docs/kbn_core_saved_objects_base_server_mocks.devdocs.json new file mode 100644 index 0000000000000..cab8d6394bd14 --- /dev/null +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.devdocs.json @@ -0,0 +1,90 @@ +{ + "id": "@kbn/core-saved-objects-base-server-mocks", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-mocks", + "id": "def-server.serializerMock", + "type": "Object", + "tags": [], + "label": "serializerMock", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-base-server-mocks/src/serializer.mock.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-mocks", + "id": "def-server.serializerMock.create", + "type": "Function", + "tags": [], + "label": "create", + "description": [], + "signature": [ + "() => jest.Mocked<", + "ISavedObjectsSerializer", + ">" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-mocks/src/serializer.mock.ts", + "deprecated": false, + "returnComment": [], + "children": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-base-server-mocks", + "id": "def-server.typeRegistryMock", + "type": "Object", + "tags": [], + "label": "typeRegistryMock", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-base-server-mocks/src/saved_objects_type_registry.mock.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-base-server-mocks", + "id": "def-server.typeRegistryMock.create", + "type": "Function", + "tags": [], + "label": "create", + "description": [], + "signature": [ + "() => jest.Mocked<", + "ISavedObjectTypeRegistry", + " & Pick<", + "SavedObjectTypeRegistry", + ", \"registerType\">>" + ], + "path": "packages/core/saved-objects/core-saved-objects-base-server-mocks/src/saved_objects_type_registry.mock.ts", + "deprecated": false, + "returnComment": [], + "children": [] + } + ], + "initialIsOpen": false + } + ] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx new file mode 100644 index 0000000000000..ac44b8eb0e6e5 --- /dev/null +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx @@ -0,0 +1,30 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnCoreSavedObjectsBaseServerMocksPluginApi +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: 2022-08-23 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-mocks'] +--- +import kbnCoreSavedObjectsBaseServerMocksObj from './kbn_core_saved_objects_base_server_mocks.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 4 | 0 | 4 | 0 | + +## Server + +### Objects + + diff --git a/api_docs/kbn_core_saved_objects_browser.mdx b/api_docs/kbn_core_saved_objects_browser.mdx index 1d5e933a96f74..5ce2261379262 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: 2022-08-19 +date: 2022-08-23 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 ea1fef66f9f45..e7d98cac1e0b5 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: 2022-08-19 +date: 2022-08-23 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 afde3fcaecb75..32f1e9fee5ec8 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: 2022-08-19 +date: 2022-08-23 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 e89732b286c67..bc5c204a26e5e 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: 2022-08-19 +date: 2022-08-23 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_server.mdx b/api_docs/kbn_core_saved_objects_server.mdx index 2fbcd5bf87168..8802b1caa8897 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: 2022-08-19 +date: 2022-08-23 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_utils_server.devdocs.json b/api_docs/kbn_core_saved_objects_utils_server.devdocs.json new file mode 100644 index 0000000000000..c8f93a8055235 --- /dev/null +++ b/api_docs/kbn_core_saved_objects_utils_server.devdocs.json @@ -0,0 +1,1836 @@ +{ + "id": "@kbn/core-saved-objects-utils-server", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers", + "type": "Class", + "tags": [], + "label": "SavedObjectsErrorHelpers", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isSavedObjectsClientError", + "type": "Function", + "tags": [], + "label": "isSavedObjectsClientError", + "description": [], + "signature": [ + "(error: any) => error is ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isSavedObjectsClientError.$1", + "type": "Any", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "any" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateBadRequestError", + "type": "Function", + "tags": [], + "label": "decorateBadRequestError", + "description": [], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateBadRequestError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateBadRequestError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.createBadRequestError", + "type": "Function", + "tags": [], + "label": "createBadRequestError", + "description": [], + "signature": [ + "(reason?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.createBadRequestError.$1", + "type": "string", + "tags": [], + "label": "reason", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.createUnsupportedTypeError", + "type": "Function", + "tags": [], + "label": "createUnsupportedTypeError", + "description": [], + "signature": [ + "(type: string) => ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.createUnsupportedTypeError.$1", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isBadRequestError", + "type": "Function", + "tags": [], + "label": "isBadRequestError", + "description": [], + "signature": [ + "(error: Error | ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + }, + ") => boolean" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isBadRequestError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error | ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.createInvalidVersionError", + "type": "Function", + "tags": [], + "label": "createInvalidVersionError", + "description": [], + "signature": [ + "(versionInput?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.createInvalidVersionError.$1", + "type": "string", + "tags": [], + "label": "versionInput", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isInvalidVersionError", + "type": "Function", + "tags": [], + "label": "isInvalidVersionError", + "description": [], + "signature": [ + "(error: Error | ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + }, + ") => boolean" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isInvalidVersionError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error | ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateNotAuthorizedError", + "type": "Function", + "tags": [], + "label": "decorateNotAuthorizedError", + "description": [], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateNotAuthorizedError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateNotAuthorizedError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isNotAuthorizedError", + "type": "Function", + "tags": [], + "label": "isNotAuthorizedError", + "description": [], + "signature": [ + "(error: Error | ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + }, + ") => boolean" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isNotAuthorizedError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error | ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateForbiddenError", + "type": "Function", + "tags": [], + "label": "decorateForbiddenError", + "description": [], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateForbiddenError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateForbiddenError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isForbiddenError", + "type": "Function", + "tags": [], + "label": "isForbiddenError", + "description": [], + "signature": [ + "(error: Error | ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + }, + ") => boolean" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isForbiddenError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error | ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateRequestEntityTooLargeError", + "type": "Function", + "tags": [], + "label": "decorateRequestEntityTooLargeError", + "description": [], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateRequestEntityTooLargeError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateRequestEntityTooLargeError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isRequestEntityTooLargeError", + "type": "Function", + "tags": [], + "label": "isRequestEntityTooLargeError", + "description": [], + "signature": [ + "(error: Error | ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + }, + ") => boolean" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isRequestEntityTooLargeError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error | ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.createGenericNotFoundError", + "type": "Function", + "tags": [], + "label": "createGenericNotFoundError", + "description": [], + "signature": [ + "(type?: string | null, id?: string | null) => ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.createGenericNotFoundError.$1", + "type": "CompoundType", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "string | null" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.createGenericNotFoundError.$2", + "type": "CompoundType", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "string | null" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.createIndexAliasNotFoundError", + "type": "Function", + "tags": [], + "label": "createIndexAliasNotFoundError", + "description": [], + "signature": [ + "(alias: string) => ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.createIndexAliasNotFoundError.$1", + "type": "string", + "tags": [], + "label": "alias", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateIndexAliasNotFoundError", + "type": "Function", + "tags": [], + "label": "decorateIndexAliasNotFoundError", + "description": [], + "signature": [ + "(error: Error, alias: string) => ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateIndexAliasNotFoundError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateIndexAliasNotFoundError.$2", + "type": "string", + "tags": [], + "label": "alias", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isNotFoundError", + "type": "Function", + "tags": [], + "label": "isNotFoundError", + "description": [], + "signature": [ + "(error: Error | ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + }, + ") => boolean" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isNotFoundError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error | ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateConflictError", + "type": "Function", + "tags": [], + "label": "decorateConflictError", + "description": [], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateConflictError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateConflictError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.createConflictError", + "type": "Function", + "tags": [], + "label": "createConflictError", + "description": [], + "signature": [ + "(type: string, id: string, reason?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.createConflictError.$1", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.createConflictError.$2", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.createConflictError.$3", + "type": "string", + "tags": [], + "label": "reason", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isConflictError", + "type": "Function", + "tags": [], + "label": "isConflictError", + "description": [], + "signature": [ + "(error: Error | ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + }, + ") => boolean" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isConflictError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error | ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateTooManyRequestsError", + "type": "Function", + "tags": [], + "label": "decorateTooManyRequestsError", + "description": [], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateTooManyRequestsError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateTooManyRequestsError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.createTooManyRequestsError", + "type": "Function", + "tags": [], + "label": "createTooManyRequestsError", + "description": [], + "signature": [ + "(type: string, id: string) => ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.createTooManyRequestsError.$1", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.createTooManyRequestsError.$2", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "string" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isTooManyRequestsError", + "type": "Function", + "tags": [], + "label": "isTooManyRequestsError", + "description": [], + "signature": [ + "(error: Error | ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + }, + ") => boolean" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isTooManyRequestsError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error | ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateEsCannotExecuteScriptError", + "type": "Function", + "tags": [], + "label": "decorateEsCannotExecuteScriptError", + "description": [], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateEsCannotExecuteScriptError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateEsCannotExecuteScriptError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isEsCannotExecuteScriptError", + "type": "Function", + "tags": [], + "label": "isEsCannotExecuteScriptError", + "description": [], + "signature": [ + "(error: Error | ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + }, + ") => boolean" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isEsCannotExecuteScriptError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error | ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateEsUnavailableError", + "type": "Function", + "tags": [], + "label": "decorateEsUnavailableError", + "description": [], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateEsUnavailableError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateEsUnavailableError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isEsUnavailableError", + "type": "Function", + "tags": [], + "label": "isEsUnavailableError", + "description": [], + "signature": [ + "(error: Error | ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + }, + ") => boolean" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isEsUnavailableError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error | ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateGeneralError", + "type": "Function", + "tags": [], + "label": "decorateGeneralError", + "description": [], + "signature": [ + "(error: Error, reason?: string | undefined) => ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateGeneralError.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.decorateGeneralError.$2", + "type": "string", + "tags": [], + "label": "reason", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isGeneralError", + "type": "Function", + "tags": [], + "label": "isGeneralError", + "description": [], + "signature": [ + "(error: Error | ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + }, + ") => boolean" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.isGeneralError.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error | ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError", + "type": "Function", + "tags": [], + "label": "createGenericNotFoundEsUnavailableError", + "description": [], + "signature": [ + "(type?: string | null, id?: string | null) => ", + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + } + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError.$1", + "type": "CompoundType", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "string | null" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError.$2", + "type": "CompoundType", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "string | null" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsUtils", + "type": "Class", + "tags": [], + "label": "SavedObjectsUtils", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsUtils.namespaceIdToString", + "type": "Function", + "tags": [], + "label": "namespaceIdToString", + "description": [ + "\nConverts a given saved object namespace ID to its string representation. All namespace IDs have an identical string representation, with\nthe exception of the `undefined` namespace ID (which has a namespace string of `'default'`).\n" + ], + "signature": [ + "(namespace?: string | undefined) => string" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsUtils.namespaceIdToString.$1", + "type": "string", + "tags": [], + "label": "namespace", + "description": [ + "The namespace ID, which must be either a non-empty string or `undefined`." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsUtils.namespaceStringToId", + "type": "Function", + "tags": [], + "label": "namespaceStringToId", + "description": [ + "\nConverts a given saved object namespace string to its ID representation. All namespace strings have an identical ID representation, with\nthe exception of the `'default'` namespace string (which has a namespace ID of `undefined`).\n" + ], + "signature": [ + "(namespace: string) => string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsUtils.namespaceStringToId.$1", + "type": "string", + "tags": [], + "label": "namespace", + "description": [ + "The namespace string, which must be non-empty." + ], + "signature": [ + "string" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsUtils.createEmptyFindResponse", + "type": "Function", + "tags": [], + "label": "createEmptyFindResponse", + "description": [ + "\nCreates an empty response for a find operation. This is only intended to be used by saved objects client wrappers." + ], + "signature": [ + "({ page, perPage, }: ", + "SavedObjectsFindOptions", + ") => ", + "SavedObjectsFindResponse", + "" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsUtils.createEmptyFindResponse.$1", + "type": "Object", + "tags": [], + "label": "{\n page = FIND_DEFAULT_PAGE,\n perPage = FIND_DEFAULT_PER_PAGE,\n }", + "description": [], + "signature": [ + "SavedObjectsFindOptions" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsUtils.generateId", + "type": "Function", + "tags": [], + "label": "generateId", + "description": [ + "\nGenerates a random ID for a saved objects." + ], + "signature": [ + "() => string" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsUtils.isRandomId", + "type": "Function", + "tags": [ + "todo" + ], + "label": "isRandomId", + "description": [ + "\nValidates that a saved object ID has been randomly generated.\n" + ], + "signature": [ + "(id: string | undefined) => boolean" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsUtils.isRandomId.$1", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "The ID of a saved object." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsUtils.getConvertedObjectId", + "type": "Function", + "tags": [], + "label": "getConvertedObjectId", + "description": [ + "\nUses a single-namespace object's \"legacy ID\" to determine what its new ID will be after it is converted to a multi-namespace type.\n" + ], + "signature": [ + "(namespace: string | undefined, type: string, id: string) => string" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsUtils.getConvertedObjectId.$1", + "type": "string", + "tags": [], + "label": "namespace", + "description": [ + "The namespace of the saved object before it is converted." + ], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.ts", + "deprecated": false, + "isRequired": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsUtils.getConvertedObjectId.$2", + "type": "string", + "tags": [], + "label": "type", + "description": [ + "The type of the saved object before it is converted." + ], + "signature": [ + "string" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.SavedObjectsUtils.getConvertedObjectId.$3", + "type": "string", + "tags": [], + "label": "id", + "description": [ + "The ID of the saved object before it is converted." + ], + "signature": [ + "string" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [ + "The ID of the saved object after it is converted." + ] + } + ], + "initialIsOpen": false + } + ], + "functions": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.mergeSavedObjectMigrationMaps", + "type": "Function", + "tags": [], + "label": "mergeSavedObjectMigrationMaps", + "description": [ + "\nMerges two saved object migration maps.\n\nIf there is a migration for a given version on only one of the maps,\nthat migration function will be used:\n\nmergeSavedObjectMigrationMaps({ '1.2.3': f }, { '4.5.6': g }) -> { '1.2.3': f, '4.5.6': g }\n\nIf there is a migration for a given version on both maps, the migrations will be composed:\n\nmergeSavedObjectMigrationMaps({ '1.2.3': f }, { '1.2.3': g }) -> { '1.2.3': (doc, context) => f(g(doc, context), context) }\n" + ], + "signature": [ + "(map1: ", + "SavedObjectMigrationMap", + ", map2: ", + "SavedObjectMigrationMap", + ") => ", + "SavedObjectMigrationMap" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/merge_migration_maps.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.mergeSavedObjectMigrationMaps.$1", + "type": "Object", + "tags": [], + "label": "map1", + "description": [], + "signature": [ + "SavedObjectMigrationMap" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/merge_migration_maps.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.mergeSavedObjectMigrationMaps.$2", + "type": "Object", + "tags": [], + "label": "map2", + "description": [], + "signature": [ + "SavedObjectMigrationMap" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/merge_migration_maps.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.DecoratedError", + "type": "Interface", + "tags": [], + "label": "DecoratedError", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-utils-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsUtilsServerPluginApi", + "section": "def-server.DecoratedError", + "text": "DecoratedError" + }, + " extends ", + "Boom", + "" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.DecoratedError.code", + "type": "string", + "tags": [], + "label": "[code]", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts", + "deprecated": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.ALL_NAMESPACES_STRING", + "type": "string", + "tags": [], + "label": "ALL_NAMESPACES_STRING", + "description": [], + "signature": [ + "\"*\"" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.DEFAULT_NAMESPACE_STRING", + "type": "string", + "tags": [], + "label": "DEFAULT_NAMESPACE_STRING", + "description": [], + "signature": [ + "\"default\"" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.FIND_DEFAULT_PAGE", + "type": "number", + "tags": [], + "label": "FIND_DEFAULT_PAGE", + "description": [], + "signature": [ + "1" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-utils-server", + "id": "def-server.FIND_DEFAULT_PER_PAGE", + "type": "number", + "tags": [], + "label": "FIND_DEFAULT_PER_PAGE", + "description": [], + "signature": [ + "20" + ], + "path": "packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.ts", + "deprecated": false, + "initialIsOpen": false + } + ], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_core_saved_objects_utils_server.mdx b/api_docs/kbn_core_saved_objects_utils_server.mdx new file mode 100644 index 0000000000000..ffa68015f2a8e --- /dev/null +++ b/api_docs/kbn_core_saved_objects_utils_server.mdx @@ -0,0 +1,39 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnCoreSavedObjectsUtilsServerPluginApi +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: 2022-08-23 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-utils-server'] +--- +import kbnCoreSavedObjectsUtilsServerObj from './kbn_core_saved_objects_utils_server.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 99 | 1 | 86 | 0 | + +## Server + +### Functions + + +### Classes + + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx index 8accf263a569b..cd3f7ee02014f 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: 2022-08-19 +date: 2022-08-23 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 30c9f0e10d85e..466ca1484cef0 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: 2022-08-19 +date: 2022-08-23 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_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index f75473adab740..d7903f7837eaa 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: 2022-08-19 +date: 2022-08-23 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 c68a4bc68825f..1d2a9dc1d6a41 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: 2022-08-19 +date: 2022-08-23 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 1c8d20565fd32..12dbdf608ba9c 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: 2022-08-19 +date: 2022-08-23 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 df9c1ded65f0f..9b247776c7b41 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: 2022-08-19 +date: 2022-08-23 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 4204ff22dab54..4e65247c886f2 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: 2022-08-19 +date: 2022-08-23 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 2f94fcf742e11..acbb6dbd2aa5f 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: 2022-08-19 +date: 2022-08-23 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 2ec0f230fc92a..cac3fb30f7879 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: 2022-08-19 +date: 2022-08-23 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_crypto.mdx b/api_docs/kbn_crypto.mdx index 22caa7b016fbe..b8534d4cd5f03 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: 2022-08-19 +date: 2022-08-23 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 a91d4e7447ae3..234d021982b6c 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index 8a5bbf6b152eb..1af482c90aa3a 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: 2022-08-19 +date: 2022-08-23 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 483cc102f3fbd..88b37840c6333 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: 2022-08-19 +date: 2022-08-23 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 0cf31f5f678a0..cee1223ae0c4d 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: 2022-08-19 +date: 2022-08-23 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 5565fb54c7a31..dd117fd11fea1 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: 2022-08-19 +date: 2022-08-23 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 1a79172d6a62d..5d47e4db459e1 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: 2022-08-19 +date: 2022-08-23 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 6f03d3ca9503f..9e71753495ee4 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: 2022-08-19 +date: 2022-08-23 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 07e4009970aac..11d35f8211dc4 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index 86be181b87f24..c166c297078d2 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index 9883411be0a28..af504b4d3fd3f 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: 2022-08-19 +date: 2022-08-23 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 b4c83fa60d620..5f09abd07fcb8 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index 4c1def9c3a2f4..224b244ed5d03 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] --- import kbnEsQueryObj from './kbn_es_query.devdocs.json'; diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index e021fd9eb8085..c0dec7ef569bb 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index 76b18fdb98d4a..82d71c69148da 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: 2022-08-19 +date: 2022-08-23 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 3383a77d849f6..2006c9b1374ae 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: 2022-08-19 +date: 2022-08-23 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_generate.mdx b/api_docs/kbn_generate.mdx index 7de3f1b0634b2..11cf62f072861 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_get_repo_files.mdx b/api_docs/kbn_get_repo_files.mdx index f73feb11a9d0e..905bd22577b02 100644 --- a/api_docs/kbn_get_repo_files.mdx +++ b/api_docs/kbn_get_repo_files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-get-repo-files title: "@kbn/get-repo-files" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/get-repo-files plugin -date: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/get-repo-files'] --- import kbnGetRepoFilesObj from './kbn_get_repo_files.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index 19cff5e1cfcd3..6786dfe63fe83 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: 2022-08-19 +date: 2022-08-23 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 bcfadc7aeda43..294bec3d803ea 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] --- import kbnHapiMocksObj from './kbn_hapi_mocks.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index 58a35c4a685f4..117b9c8cf4811 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: 2022-08-19 +date: 2022-08-23 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 7c76f709fe49e..e253257c48576 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: 2022-08-19 +date: 2022-08-23 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 57788dbcc1f00..93d499185343e 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] --- import kbnI18nObj from './kbn_i18n.devdocs.json'; diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index 1beeff23c9b03..0f2ca6a0d292b 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: 2022-08-19 +date: 2022-08-23 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 5718691958436..ee831bb9aacf5 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: 2022-08-19 +date: 2022-08-23 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 72d92913591db..2140abbfe24f0 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: 2022-08-19 +date: 2022-08-23 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 603585ea3423f..9b23301793cec 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/jest-serializers'] --- import kbnJestSerializersObj from './kbn_jest_serializers.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_parser.mdx b/api_docs/kbn_kibana_manifest_parser.mdx index f0a381b23b8be..3b722b407667f 100644 --- a/api_docs/kbn_kibana_manifest_parser.mdx +++ b/api_docs/kbn_kibana_manifest_parser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-parser title: "@kbn/kibana-manifest-parser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-parser plugin -date: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-parser'] --- import kbnKibanaManifestParserObj from './kbn_kibana_manifest_parser.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index d93af07aee34f..f3cd5025f58c0 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index 2ee5f818c0588..7f7731f3a1a6f 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: 2022-08-19 +date: 2022-08-23 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 0fadf392c400f..73892bcdc0f26 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: 2022-08-19 +date: 2022-08-23 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 043c2e005fb10..f62b52199eee4 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: 2022-08-19 +date: 2022-08-23 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 8f06ddbb4f268..4866ae9a6c8e2 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: 2022-08-19 +date: 2022-08-23 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 2fb89402b57ea..19e96ece2e666 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: 2022-08-19 +date: 2022-08-23 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_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index d53d51c559166..be237c485aef5 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: 2022-08-19 +date: 2022-08-23 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_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index 6aae3d8321c24..9f20330ae1c82 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index c7da0989034f2..a22806a7c48f3 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 389483e8a0792..48a6e16b60147 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: 2022-08-19 +date: 2022-08-23 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 751d9cdac6dcd..4271f565698ff 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer-webpack-helpers'] --- import kbnOptimizerWebpackHelpersObj from './kbn_optimizer_webpack_helpers.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index 738c816f89603..4d5edfeb689b7 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: 2022-08-19 +date: 2022-08-23 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 a3226cc4bb3c7..156876b51ce01 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: 2022-08-19 +date: 2022-08-23 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 514beff61a6bf..f739cbb87bdac 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: 2022-08-19 +date: 2022-08-23 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 9ecaa37b6fd23..dcd05fd6dd2b8 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index 93386124f941f..b0f4bfcad8fc6 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index c080c2c3ac0db..7998b7126cc1a 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 677d2aabddef5..27ce322f9de31 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index 0f530cdac284c..9a0d2e0532fcb 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: 2022-08-19 +date: 2022-08-23 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_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index e63ca50365862..522dea862a690 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: 2022-08-19 +date: 2022-08-23 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 83095869e0681..acc55514cc7da 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: 2022-08-19 +date: 2022-08-23 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 8ad78ad8f70f1..46951fcb3c8e3 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: 2022-08-19 +date: 2022-08-23 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 e78aadbe149d3..9c59ecccfacdf 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: 2022-08-19 +date: 2022-08-23 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 26e858158e506..7a2d51225de14 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: 2022-08-19 +date: 2022-08-23 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 41d7176239914..a5368983a9037 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: 2022-08-19 +date: 2022-08-23 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 4a89b39dfc3cd..a24dc3fdcfac7 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: 2022-08-19 +date: 2022-08-23 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 d9dd3cc6d95f7..e27861b3dca99 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: 2022-08-19 +date: 2022-08-23 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 739b5a4ae8a61..cc48e0eae5762 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: 2022-08-19 +date: 2022-08-23 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 29c0c4fcd85d9..766fffe7c6984 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: 2022-08-19 +date: 2022-08-23 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 ebb314ad5aa8d..3b9e2f5a0c157 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: 2022-08-19 +date: 2022-08-23 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 9cdb59e3a6f2e..5161d40720335 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: 2022-08-19 +date: 2022-08-23 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 da871d548452c..7152c8babccbe 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: 2022-08-19 +date: 2022-08-23 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 8b82693ae6c20..3306936f182c8 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: 2022-08-19 +date: 2022-08-23 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 62d53df58f0ee..7912c2f146a1c 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-svg'] --- import kbnSharedSvgObj from './kbn_shared_svg.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 ee9b6c0315bd2..1067fe9472d69 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: 2022-08-19 +date: 2022-08-23 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 4267090256a66..5e9a63e81c31a 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: 2022-08-19 +date: 2022-08-23 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.devdocs.json b/api_docs/kbn_shared_ux_card_no_data.devdocs.json index 480849c369f6b..37fb7c13e4e9d 100644 --- a/api_docs/kbn_shared_ux_card_no_data.devdocs.json +++ b/api_docs/kbn_shared_ux_card_no_data.devdocs.json @@ -178,7 +178,7 @@ "signature": [ "{ children?: React.ReactNode; onError?: React.ReactEventHandler | undefined; hidden?: boolean | undefined; icon?: React.ReactElement<", "EuiIconProps", - ", string | React.JSXElementConstructor> | null | undefined; image?: string | React.ReactElement> | undefined; className?: string | undefined; title?: boolean | React.ReactChild | React.ReactFragment | React.ReactPortal | undefined; onChange?: React.FormEventHandler | undefined; onKeyDown?: React.KeyboardEventHandler | undefined; onClick?: React.MouseEventHandler | undefined; id?: string | undefined; description?: React.ReactNode; security?: string | undefined; defaultValue?: string | number | readonly string[] | undefined; lang?: string | undefined; category?: string | undefined; defaultChecked?: boolean | undefined; suppressContentEditableWarning?: boolean | undefined; suppressHydrationWarning?: boolean | undefined; accessKey?: string | undefined; contentEditable?: \"inherit\" | Booleanish | undefined; contextMenu?: string | undefined; dir?: string | undefined; draggable?: Booleanish | undefined; placeholder?: string | undefined; slot?: string | undefined; spellCheck?: Booleanish | undefined; style?: React.CSSProperties | undefined; tabIndex?: number | undefined; translate?: \"no\" | \"yes\" | undefined; radioGroup?: string | undefined; role?: React.AriaRole | undefined; about?: string | undefined; datatype?: string | undefined; inlist?: any; prefix?: string | undefined; property?: string | undefined; resource?: string | undefined; typeof?: string | undefined; vocab?: string | undefined; autoCapitalize?: string | undefined; autoCorrect?: string | undefined; autoSave?: string | undefined; itemProp?: string | undefined; itemScope?: boolean | undefined; itemType?: string | undefined; itemID?: string | undefined; itemRef?: string | undefined; results?: number | undefined; unselectable?: \"on\" | \"off\" | undefined; inputMode?: \"none\" | \"email\" | \"search\" | \"text\" | \"tel\" | \"url\" | \"numeric\" | \"decimal\" | undefined; is?: string | undefined; 'aria-activedescendant'?: string | undefined; 'aria-atomic'?: boolean | \"false\" | \"true\" | undefined; 'aria-autocomplete'?: \"none\" | \"list\" | \"inline\" | \"both\" | undefined; 'aria-busy'?: boolean | \"false\" | \"true\" | undefined; 'aria-checked'?: boolean | \"mixed\" | \"false\" | \"true\" | undefined; 'aria-colcount'?: number | undefined; 'aria-colindex'?: number | undefined; 'aria-colspan'?: number | undefined; 'aria-controls'?: string | undefined; 'aria-current'?: boolean | \"date\" | \"location\" | \"time\" | \"page\" | \"false\" | \"true\" | \"step\" | undefined; 'aria-describedby'?: string | undefined; 'aria-details'?: string | undefined; 'aria-disabled'?: boolean | \"false\" | \"true\" | undefined; 'aria-dropeffect'?: \"none\" | \"copy\" | \"link\" | \"execute\" | \"move\" | \"popup\" | undefined; 'aria-errormessage'?: string | undefined; 'aria-expanded'?: boolean | \"false\" | \"true\" | undefined; 'aria-flowto'?: string | undefined; 'aria-grabbed'?: boolean | \"false\" | \"true\" | undefined; 'aria-haspopup'?: boolean | \"grid\" | \"menu\" | \"false\" | \"true\" | \"dialog\" | \"listbox\" | \"tree\" | undefined; 'aria-hidden'?: boolean | \"false\" | \"true\" | undefined; 'aria-invalid'?: boolean | \"false\" | \"true\" | \"grammar\" | \"spelling\" | undefined; 'aria-keyshortcuts'?: string | undefined; 'aria-label'?: string | undefined; 'aria-labelledby'?: string | undefined; 'aria-level'?: number | undefined; 'aria-live'?: \"off\" | \"assertive\" | \"polite\" | undefined; 'aria-modal'?: boolean | \"false\" | \"true\" | undefined; 'aria-multiline'?: boolean | \"false\" | \"true\" | undefined; 'aria-multiselectable'?: boolean | \"false\" | \"true\" | undefined; 'aria-orientation'?: \"horizontal\" | \"vertical\" | undefined; 'aria-owns'?: string | undefined; 'aria-placeholder'?: string | undefined; 'aria-posinset'?: number | undefined; 'aria-pressed'?: boolean | \"mixed\" | \"false\" | \"true\" | undefined; 'aria-readonly'?: boolean | \"false\" | \"true\" | undefined; 'aria-relevant'?: \"all\" | \"text\" | \"additions\" | \"additions removals\" | \"additions text\" | \"removals\" | \"removals additions\" | \"removals text\" | \"text additions\" | \"text removals\" | undefined; 'aria-required'?: boolean | \"false\" | \"true\" | undefined; 'aria-roledescription'?: string | undefined; 'aria-rowcount'?: number | undefined; 'aria-rowindex'?: number | undefined; 'aria-rowspan'?: number | undefined; 'aria-selected'?: boolean | \"false\" | \"true\" | undefined; 'aria-setsize'?: number | undefined; 'aria-sort'?: \"none\" | \"other\" | \"ascending\" | \"descending\" | undefined; 'aria-valuemax'?: number | undefined; 'aria-valuemin'?: number | undefined; 'aria-valuenow'?: number | undefined; 'aria-valuetext'?: string | undefined; dangerouslySetInnerHTML?: { __html: string; } | undefined; onCopy?: React.ClipboardEventHandler | undefined; onCopyCapture?: React.ClipboardEventHandler | undefined; onCut?: React.ClipboardEventHandler | undefined; onCutCapture?: React.ClipboardEventHandler | undefined; onPaste?: React.ClipboardEventHandler | undefined; onPasteCapture?: React.ClipboardEventHandler | undefined; onCompositionEnd?: React.CompositionEventHandler | undefined; onCompositionEndCapture?: React.CompositionEventHandler | undefined; onCompositionStart?: React.CompositionEventHandler | undefined; onCompositionStartCapture?: React.CompositionEventHandler | undefined; onCompositionUpdate?: React.CompositionEventHandler | undefined; onCompositionUpdateCapture?: React.CompositionEventHandler | undefined; onFocus?: React.FocusEventHandler | undefined; onFocusCapture?: React.FocusEventHandler | undefined; onBlur?: React.FocusEventHandler | undefined; onBlurCapture?: React.FocusEventHandler | undefined; onChangeCapture?: React.FormEventHandler | undefined; onBeforeInput?: React.FormEventHandler | undefined; onBeforeInputCapture?: React.FormEventHandler | undefined; onInput?: React.FormEventHandler | undefined; onInputCapture?: React.FormEventHandler | undefined; onReset?: React.FormEventHandler | undefined; onResetCapture?: React.FormEventHandler | undefined; onSubmit?: React.FormEventHandler | undefined; onSubmitCapture?: React.FormEventHandler | undefined; onInvalid?: React.FormEventHandler | undefined; onInvalidCapture?: React.FormEventHandler | undefined; onLoad?: React.ReactEventHandler | undefined; onLoadCapture?: React.ReactEventHandler | undefined; onErrorCapture?: React.ReactEventHandler | undefined; onKeyDownCapture?: React.KeyboardEventHandler | undefined; onKeyPress?: React.KeyboardEventHandler | undefined; onKeyPressCapture?: React.KeyboardEventHandler | undefined; onKeyUp?: React.KeyboardEventHandler | undefined; onKeyUpCapture?: React.KeyboardEventHandler | undefined; onAbort?: React.ReactEventHandler | undefined; onAbortCapture?: React.ReactEventHandler | undefined; onCanPlay?: React.ReactEventHandler | undefined; onCanPlayCapture?: React.ReactEventHandler | undefined; onCanPlayThrough?: React.ReactEventHandler | undefined; onCanPlayThroughCapture?: React.ReactEventHandler | undefined; onDurationChange?: React.ReactEventHandler | undefined; onDurationChangeCapture?: React.ReactEventHandler | undefined; onEmptied?: React.ReactEventHandler | undefined; onEmptiedCapture?: React.ReactEventHandler | undefined; onEncrypted?: React.ReactEventHandler | undefined; onEncryptedCapture?: React.ReactEventHandler | undefined; onEnded?: React.ReactEventHandler | undefined; onEndedCapture?: React.ReactEventHandler | undefined; onLoadedData?: React.ReactEventHandler | undefined; onLoadedDataCapture?: React.ReactEventHandler | undefined; onLoadedMetadata?: React.ReactEventHandler | undefined; onLoadedMetadataCapture?: React.ReactEventHandler | undefined; onLoadStart?: React.ReactEventHandler | undefined; onLoadStartCapture?: React.ReactEventHandler | undefined; onPause?: React.ReactEventHandler | undefined; onPauseCapture?: React.ReactEventHandler | undefined; onPlay?: React.ReactEventHandler | undefined; onPlayCapture?: React.ReactEventHandler | undefined; onPlaying?: React.ReactEventHandler | undefined; onPlayingCapture?: React.ReactEventHandler | undefined; onProgress?: React.ReactEventHandler | undefined; onProgressCapture?: React.ReactEventHandler | undefined; onRateChange?: React.ReactEventHandler | undefined; onRateChangeCapture?: React.ReactEventHandler | undefined; onSeeked?: React.ReactEventHandler | undefined; onSeekedCapture?: React.ReactEventHandler | undefined; onSeeking?: React.ReactEventHandler | undefined; onSeekingCapture?: React.ReactEventHandler | undefined; onStalled?: React.ReactEventHandler | undefined; onStalledCapture?: React.ReactEventHandler | undefined; onSuspend?: React.ReactEventHandler | undefined; onSuspendCapture?: React.ReactEventHandler | undefined; onTimeUpdate?: React.ReactEventHandler | undefined; onTimeUpdateCapture?: React.ReactEventHandler | undefined; onVolumeChange?: React.ReactEventHandler | undefined; onVolumeChangeCapture?: React.ReactEventHandler | undefined; onWaiting?: React.ReactEventHandler | undefined; onWaitingCapture?: React.ReactEventHandler | undefined; onAuxClick?: React.MouseEventHandler | undefined; onAuxClickCapture?: React.MouseEventHandler | undefined; onClickCapture?: React.MouseEventHandler | undefined; onContextMenu?: React.MouseEventHandler | undefined; onContextMenuCapture?: React.MouseEventHandler | undefined; onDoubleClick?: React.MouseEventHandler | undefined; onDoubleClickCapture?: React.MouseEventHandler | undefined; onDrag?: React.DragEventHandler | undefined; onDragCapture?: React.DragEventHandler | undefined; onDragEnd?: React.DragEventHandler | undefined; onDragEndCapture?: React.DragEventHandler | undefined; onDragEnter?: React.DragEventHandler | undefined; onDragEnterCapture?: React.DragEventHandler | undefined; onDragExit?: React.DragEventHandler | undefined; onDragExitCapture?: React.DragEventHandler | undefined; onDragLeave?: React.DragEventHandler | undefined; onDragLeaveCapture?: React.DragEventHandler | undefined; onDragOver?: React.DragEventHandler | undefined; onDragOverCapture?: React.DragEventHandler | undefined; onDragStart?: React.DragEventHandler | undefined; onDragStartCapture?: React.DragEventHandler | undefined; onDrop?: React.DragEventHandler | undefined; onDropCapture?: React.DragEventHandler | undefined; onMouseDown?: React.MouseEventHandler | undefined; onMouseDownCapture?: React.MouseEventHandler | undefined; onMouseEnter?: React.MouseEventHandler | undefined; onMouseLeave?: React.MouseEventHandler | undefined; onMouseMove?: React.MouseEventHandler | undefined; onMouseMoveCapture?: React.MouseEventHandler | undefined; onMouseOut?: React.MouseEventHandler | undefined; onMouseOutCapture?: React.MouseEventHandler | undefined; onMouseOver?: React.MouseEventHandler | undefined; onMouseOverCapture?: React.MouseEventHandler | undefined; onMouseUp?: React.MouseEventHandler | undefined; onMouseUpCapture?: React.MouseEventHandler | undefined; onSelect?: React.ReactEventHandler | undefined; onSelectCapture?: React.ReactEventHandler | undefined; onTouchCancel?: React.TouchEventHandler | undefined; onTouchCancelCapture?: React.TouchEventHandler | undefined; onTouchEnd?: React.TouchEventHandler | undefined; onTouchEndCapture?: React.TouchEventHandler | undefined; onTouchMove?: React.TouchEventHandler | undefined; onTouchMoveCapture?: React.TouchEventHandler | undefined; onTouchStart?: React.TouchEventHandler | undefined; onTouchStartCapture?: React.TouchEventHandler | undefined; onPointerDown?: React.PointerEventHandler | undefined; onPointerDownCapture?: React.PointerEventHandler | undefined; onPointerMove?: React.PointerEventHandler | undefined; onPointerMoveCapture?: React.PointerEventHandler | undefined; onPointerUp?: React.PointerEventHandler | undefined; onPointerUpCapture?: React.PointerEventHandler | undefined; onPointerCancel?: React.PointerEventHandler | undefined; onPointerCancelCapture?: React.PointerEventHandler | undefined; onPointerEnter?: React.PointerEventHandler | undefined; onPointerEnterCapture?: React.PointerEventHandler | undefined; onPointerLeave?: React.PointerEventHandler | undefined; onPointerLeaveCapture?: React.PointerEventHandler | undefined; onPointerOver?: React.PointerEventHandler | undefined; onPointerOverCapture?: React.PointerEventHandler | undefined; onPointerOut?: React.PointerEventHandler | undefined; onPointerOutCapture?: React.PointerEventHandler | undefined; onGotPointerCapture?: React.PointerEventHandler | undefined; onGotPointerCaptureCapture?: React.PointerEventHandler | undefined; onLostPointerCapture?: React.PointerEventHandler | undefined; onLostPointerCaptureCapture?: React.PointerEventHandler | undefined; onScroll?: React.UIEventHandler | undefined; onScrollCapture?: React.UIEventHandler | undefined; onWheel?: React.WheelEventHandler | undefined; onWheelCapture?: React.WheelEventHandler | undefined; onAnimationStart?: React.AnimationEventHandler | undefined; onAnimationStartCapture?: React.AnimationEventHandler | undefined; onAnimationEnd?: React.AnimationEventHandler | undefined; onAnimationEndCapture?: React.AnimationEventHandler | undefined; onAnimationIteration?: React.AnimationEventHandler | undefined; onAnimationIterationCapture?: React.AnimationEventHandler | undefined; onTransitionEnd?: React.TransitionEventHandler | undefined; onTransitionEndCapture?: React.TransitionEventHandler | undefined; 'data-test-subj'?: string | undefined; href?: string | undefined; rel?: string | undefined; target?: string | undefined; paddingSize?: \"none\" | \"m\" | \"s\" | \"xs\" | \"l\" | \"xl\" | undefined; button?: React.ReactNode; footer?: React.ReactNode; hasBorder?: boolean | undefined; textAlign?: CardAlignment | undefined; titleElement?: \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"span\" | undefined; titleSize?: \"s\" | \"xs\" | undefined; betaBadgeProps?: Partial<(", + ", string | React.JSXElementConstructor> | null | undefined; image?: string | React.ReactElement> | undefined; className?: string | undefined; title?: boolean | React.ReactChild | React.ReactFragment | React.ReactPortal | undefined; onChange?: React.FormEventHandler | undefined; onKeyDown?: React.KeyboardEventHandler | undefined; onClick?: React.MouseEventHandler | undefined; id?: string | undefined; description?: React.ReactNode; security?: string | undefined; defaultValue?: string | number | readonly string[] | undefined; lang?: string | undefined; category?: string | undefined; defaultChecked?: boolean | undefined; suppressContentEditableWarning?: boolean | undefined; suppressHydrationWarning?: boolean | undefined; accessKey?: string | undefined; contentEditable?: \"inherit\" | Booleanish | undefined; contextMenu?: string | undefined; dir?: string | undefined; draggable?: Booleanish | undefined; placeholder?: string | undefined; slot?: string | undefined; spellCheck?: Booleanish | undefined; style?: React.CSSProperties | undefined; tabIndex?: number | undefined; translate?: \"no\" | \"yes\" | undefined; radioGroup?: string | undefined; role?: React.AriaRole | undefined; about?: string | undefined; datatype?: string | undefined; inlist?: any; prefix?: string | undefined; property?: string | undefined; resource?: string | undefined; typeof?: string | undefined; vocab?: string | undefined; autoCapitalize?: string | undefined; autoCorrect?: string | undefined; autoSave?: string | undefined; itemProp?: string | undefined; itemScope?: boolean | undefined; itemType?: string | undefined; itemID?: string | undefined; itemRef?: string | undefined; results?: number | undefined; unselectable?: \"on\" | \"off\" | undefined; inputMode?: \"none\" | \"email\" | \"search\" | \"text\" | \"tel\" | \"url\" | \"numeric\" | \"decimal\" | undefined; is?: string | undefined; 'aria-activedescendant'?: string | undefined; 'aria-atomic'?: Booleanish | undefined; 'aria-autocomplete'?: \"none\" | \"list\" | \"inline\" | \"both\" | undefined; 'aria-busy'?: Booleanish | undefined; 'aria-checked'?: boolean | \"mixed\" | \"false\" | \"true\" | undefined; 'aria-colcount'?: number | undefined; 'aria-colindex'?: number | undefined; 'aria-colspan'?: number | undefined; 'aria-controls'?: string | undefined; 'aria-current'?: boolean | \"date\" | \"location\" | \"time\" | \"page\" | \"false\" | \"true\" | \"step\" | undefined; 'aria-describedby'?: string | undefined; 'aria-details'?: string | undefined; 'aria-disabled'?: Booleanish | undefined; 'aria-dropeffect'?: \"none\" | \"copy\" | \"link\" | \"execute\" | \"move\" | \"popup\" | undefined; 'aria-errormessage'?: string | undefined; 'aria-expanded'?: Booleanish | undefined; 'aria-flowto'?: string | undefined; 'aria-grabbed'?: Booleanish | undefined; 'aria-haspopup'?: boolean | \"grid\" | \"menu\" | \"false\" | \"true\" | \"dialog\" | \"listbox\" | \"tree\" | undefined; 'aria-hidden'?: Booleanish | undefined; 'aria-invalid'?: boolean | \"false\" | \"true\" | \"grammar\" | \"spelling\" | undefined; 'aria-keyshortcuts'?: string | undefined; 'aria-label'?: string | undefined; 'aria-labelledby'?: string | undefined; 'aria-level'?: number | undefined; 'aria-live'?: \"off\" | \"assertive\" | \"polite\" | undefined; 'aria-modal'?: Booleanish | undefined; 'aria-multiline'?: Booleanish | undefined; 'aria-multiselectable'?: Booleanish | undefined; 'aria-orientation'?: \"horizontal\" | \"vertical\" | undefined; 'aria-owns'?: string | undefined; 'aria-placeholder'?: string | undefined; 'aria-posinset'?: number | undefined; 'aria-pressed'?: boolean | \"mixed\" | \"false\" | \"true\" | undefined; 'aria-readonly'?: Booleanish | undefined; 'aria-relevant'?: \"all\" | \"text\" | \"additions\" | \"additions removals\" | \"additions text\" | \"removals\" | \"removals additions\" | \"removals text\" | \"text additions\" | \"text removals\" | undefined; 'aria-required'?: Booleanish | undefined; 'aria-roledescription'?: string | undefined; 'aria-rowcount'?: number | undefined; 'aria-rowindex'?: number | undefined; 'aria-rowspan'?: number | undefined; 'aria-selected'?: Booleanish | undefined; 'aria-setsize'?: number | undefined; 'aria-sort'?: \"none\" | \"other\" | \"ascending\" | \"descending\" | undefined; 'aria-valuemax'?: number | undefined; 'aria-valuemin'?: number | undefined; 'aria-valuenow'?: number | undefined; 'aria-valuetext'?: string | undefined; dangerouslySetInnerHTML?: { __html: string; } | undefined; onCopy?: React.ClipboardEventHandler | undefined; onCopyCapture?: React.ClipboardEventHandler | undefined; onCut?: React.ClipboardEventHandler | undefined; onCutCapture?: React.ClipboardEventHandler | undefined; onPaste?: React.ClipboardEventHandler | undefined; onPasteCapture?: React.ClipboardEventHandler | undefined; onCompositionEnd?: React.CompositionEventHandler | undefined; onCompositionEndCapture?: React.CompositionEventHandler | undefined; onCompositionStart?: React.CompositionEventHandler | undefined; onCompositionStartCapture?: React.CompositionEventHandler | undefined; onCompositionUpdate?: React.CompositionEventHandler | undefined; onCompositionUpdateCapture?: React.CompositionEventHandler | undefined; onFocus?: React.FocusEventHandler | undefined; onFocusCapture?: React.FocusEventHandler | undefined; onBlur?: React.FocusEventHandler | undefined; onBlurCapture?: React.FocusEventHandler | undefined; onChangeCapture?: React.FormEventHandler | undefined; onBeforeInput?: React.FormEventHandler | undefined; onBeforeInputCapture?: React.FormEventHandler | undefined; onInput?: React.FormEventHandler | undefined; onInputCapture?: React.FormEventHandler | undefined; onReset?: React.FormEventHandler | undefined; onResetCapture?: React.FormEventHandler | undefined; onSubmit?: React.FormEventHandler | undefined; onSubmitCapture?: React.FormEventHandler | undefined; onInvalid?: React.FormEventHandler | undefined; onInvalidCapture?: React.FormEventHandler | undefined; onLoad?: React.ReactEventHandler | undefined; onLoadCapture?: React.ReactEventHandler | undefined; onErrorCapture?: React.ReactEventHandler | undefined; onKeyDownCapture?: React.KeyboardEventHandler | undefined; onKeyPress?: React.KeyboardEventHandler | undefined; onKeyPressCapture?: React.KeyboardEventHandler | undefined; onKeyUp?: React.KeyboardEventHandler | undefined; onKeyUpCapture?: React.KeyboardEventHandler | undefined; onAbort?: React.ReactEventHandler | undefined; onAbortCapture?: React.ReactEventHandler | undefined; onCanPlay?: React.ReactEventHandler | undefined; onCanPlayCapture?: React.ReactEventHandler | undefined; onCanPlayThrough?: React.ReactEventHandler | undefined; onCanPlayThroughCapture?: React.ReactEventHandler | undefined; onDurationChange?: React.ReactEventHandler | undefined; onDurationChangeCapture?: React.ReactEventHandler | undefined; onEmptied?: React.ReactEventHandler | undefined; onEmptiedCapture?: React.ReactEventHandler | undefined; onEncrypted?: React.ReactEventHandler | undefined; onEncryptedCapture?: React.ReactEventHandler | undefined; onEnded?: React.ReactEventHandler | undefined; onEndedCapture?: React.ReactEventHandler | undefined; onLoadedData?: React.ReactEventHandler | undefined; onLoadedDataCapture?: React.ReactEventHandler | undefined; onLoadedMetadata?: React.ReactEventHandler | undefined; onLoadedMetadataCapture?: React.ReactEventHandler | undefined; onLoadStart?: React.ReactEventHandler | undefined; onLoadStartCapture?: React.ReactEventHandler | undefined; onPause?: React.ReactEventHandler | undefined; onPauseCapture?: React.ReactEventHandler | undefined; onPlay?: React.ReactEventHandler | undefined; onPlayCapture?: React.ReactEventHandler | undefined; onPlaying?: React.ReactEventHandler | undefined; onPlayingCapture?: React.ReactEventHandler | undefined; onProgress?: React.ReactEventHandler | undefined; onProgressCapture?: React.ReactEventHandler | undefined; onRateChange?: React.ReactEventHandler | undefined; onRateChangeCapture?: React.ReactEventHandler | undefined; onSeeked?: React.ReactEventHandler | undefined; onSeekedCapture?: React.ReactEventHandler | undefined; onSeeking?: React.ReactEventHandler | undefined; onSeekingCapture?: React.ReactEventHandler | undefined; onStalled?: React.ReactEventHandler | undefined; onStalledCapture?: React.ReactEventHandler | undefined; onSuspend?: React.ReactEventHandler | undefined; onSuspendCapture?: React.ReactEventHandler | undefined; onTimeUpdate?: React.ReactEventHandler | undefined; onTimeUpdateCapture?: React.ReactEventHandler | undefined; onVolumeChange?: React.ReactEventHandler | undefined; onVolumeChangeCapture?: React.ReactEventHandler | undefined; onWaiting?: React.ReactEventHandler | undefined; onWaitingCapture?: React.ReactEventHandler | undefined; onAuxClick?: React.MouseEventHandler | undefined; onAuxClickCapture?: React.MouseEventHandler | undefined; onClickCapture?: React.MouseEventHandler | undefined; onContextMenu?: React.MouseEventHandler | undefined; onContextMenuCapture?: React.MouseEventHandler | undefined; onDoubleClick?: React.MouseEventHandler | undefined; onDoubleClickCapture?: React.MouseEventHandler | undefined; onDrag?: React.DragEventHandler | undefined; onDragCapture?: React.DragEventHandler | undefined; onDragEnd?: React.DragEventHandler | undefined; onDragEndCapture?: React.DragEventHandler | undefined; onDragEnter?: React.DragEventHandler | undefined; onDragEnterCapture?: React.DragEventHandler | undefined; onDragExit?: React.DragEventHandler | undefined; onDragExitCapture?: React.DragEventHandler | undefined; onDragLeave?: React.DragEventHandler | undefined; onDragLeaveCapture?: React.DragEventHandler | undefined; onDragOver?: React.DragEventHandler | undefined; onDragOverCapture?: React.DragEventHandler | undefined; onDragStart?: React.DragEventHandler | undefined; onDragStartCapture?: React.DragEventHandler | undefined; onDrop?: React.DragEventHandler | undefined; onDropCapture?: React.DragEventHandler | undefined; onMouseDown?: React.MouseEventHandler | undefined; onMouseDownCapture?: React.MouseEventHandler | undefined; onMouseEnter?: React.MouseEventHandler | undefined; onMouseLeave?: React.MouseEventHandler | undefined; onMouseMove?: React.MouseEventHandler | undefined; onMouseMoveCapture?: React.MouseEventHandler | undefined; onMouseOut?: React.MouseEventHandler | undefined; onMouseOutCapture?: React.MouseEventHandler | undefined; onMouseOver?: React.MouseEventHandler | undefined; onMouseOverCapture?: React.MouseEventHandler | undefined; onMouseUp?: React.MouseEventHandler | undefined; onMouseUpCapture?: React.MouseEventHandler | undefined; onSelect?: React.ReactEventHandler | undefined; onSelectCapture?: React.ReactEventHandler | undefined; onTouchCancel?: React.TouchEventHandler | undefined; onTouchCancelCapture?: React.TouchEventHandler | undefined; onTouchEnd?: React.TouchEventHandler | undefined; onTouchEndCapture?: React.TouchEventHandler | undefined; onTouchMove?: React.TouchEventHandler | undefined; onTouchMoveCapture?: React.TouchEventHandler | undefined; onTouchStart?: React.TouchEventHandler | undefined; onTouchStartCapture?: React.TouchEventHandler | undefined; onPointerDown?: React.PointerEventHandler | undefined; onPointerDownCapture?: React.PointerEventHandler | undefined; onPointerMove?: React.PointerEventHandler | undefined; onPointerMoveCapture?: React.PointerEventHandler | undefined; onPointerUp?: React.PointerEventHandler | undefined; onPointerUpCapture?: React.PointerEventHandler | undefined; onPointerCancel?: React.PointerEventHandler | undefined; onPointerCancelCapture?: React.PointerEventHandler | undefined; onPointerEnter?: React.PointerEventHandler | undefined; onPointerEnterCapture?: React.PointerEventHandler | undefined; onPointerLeave?: React.PointerEventHandler | undefined; onPointerLeaveCapture?: React.PointerEventHandler | undefined; onPointerOver?: React.PointerEventHandler | undefined; onPointerOverCapture?: React.PointerEventHandler | undefined; onPointerOut?: React.PointerEventHandler | undefined; onPointerOutCapture?: React.PointerEventHandler | undefined; onGotPointerCapture?: React.PointerEventHandler | undefined; onGotPointerCaptureCapture?: React.PointerEventHandler | undefined; onLostPointerCapture?: React.PointerEventHandler | undefined; onLostPointerCaptureCapture?: React.PointerEventHandler | undefined; onScroll?: React.UIEventHandler | undefined; onScrollCapture?: React.UIEventHandler | undefined; onWheel?: React.WheelEventHandler | undefined; onWheelCapture?: React.WheelEventHandler | undefined; onAnimationStart?: React.AnimationEventHandler | undefined; onAnimationStartCapture?: React.AnimationEventHandler | undefined; onAnimationEnd?: React.AnimationEventHandler | undefined; onAnimationEndCapture?: React.AnimationEventHandler | undefined; onAnimationIteration?: React.AnimationEventHandler | undefined; onAnimationIterationCapture?: React.AnimationEventHandler | undefined; onTransitionEnd?: React.TransitionEventHandler | undefined; onTransitionEndCapture?: React.TransitionEventHandler | undefined; 'data-test-subj'?: string | undefined; href?: string | undefined; rel?: string | undefined; target?: string | undefined; paddingSize?: \"none\" | \"m\" | \"s\" | \"xs\" | \"l\" | \"xl\" | undefined; button?: React.ReactNode; footer?: React.ReactNode; hasBorder?: boolean | undefined; textAlign?: CardAlignment | undefined; titleElement?: \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"span\" | undefined; titleSize?: \"s\" | \"xs\" | undefined; betaBadgeProps?: Partial<(", "CommonProps", " & ", "DisambiguateSet", diff --git a/api_docs/kbn_shared_ux_card_no_data.mdx b/api_docs/kbn_shared_ux_card_no_data.mdx index 8f86541c33698..de08de5f306a8 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: 2022-08-19 +date: 2022-08-23 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 bf6bf82adbe52..66053a3230fab 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: 2022-08-19 +date: 2022-08-23 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_link_redirect_app_mocks.mdx b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx index cf91cce9c78f1..e3e18ac5eabc5 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: 2022-08-19 +date: 2022-08-23 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_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index bf74e43351a26..ec9c4d2d460f5 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: 2022-08-19 +date: 2022-08-23 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 cf8a82bce4515..ada46b22952f5 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: 2022-08-19 +date: 2022-08-23 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 16c4cd45767f4..6ca0ea6af35f5 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: 2022-08-19 +date: 2022-08-23 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 d939a7ec9a023..bde0ff05b238c 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: 2022-08-19 +date: 2022-08-23 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 d4541386eca1b..6edc2ba554481 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: 2022-08-19 +date: 2022-08-23 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 054aa70875ade..c3541c1869b5a 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: 2022-08-19 +date: 2022-08-23 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 0edbf9e0e6117..33380cff2d7d5 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: 2022-08-19 +date: 2022-08-23 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 3a9812d7de502..45c915c5d9834 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: 2022-08-19 +date: 2022-08-23 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 b09f5d32898a6..02fcad4f1afb5 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: 2022-08-19 +date: 2022-08-23 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 691ebd95c1f38..11eda5cd60417 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: 2022-08-19 +date: 2022-08-23 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 f07a636668035..4887ce3399ad1 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: 2022-08-19 +date: 2022-08-23 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 3962645073e7d..5d623d390024f 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: 2022-08-19 +date: 2022-08-23 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 aaac0afbeed85..ecea103b5f7a7 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: 2022-08-19 +date: 2022-08-23 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_storybook_mock.mdx b/api_docs/kbn_shared_ux_storybook_mock.mdx index 3307ad0cbcb27..af568a1712ce2 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: 2022-08-19 +date: 2022-08-23 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 eb9377cb31c8b..a9b5584afe6f4 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index df75c48ebe964..a526f65364898 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_sort_package_json.mdx b/api_docs/kbn_sort_package_json.mdx index 65ccf2a186550..0214de673ddd1 100644 --- a/api_docs/kbn_sort_package_json.mdx +++ b/api_docs/kbn_sort_package_json.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sort-package-json title: "@kbn/sort-package-json" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sort-package-json plugin -date: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-package-json'] --- import kbnSortPackageJsonObj from './kbn_sort_package_json.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index cad19bf43dd58..ae55721fc807c 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: 2022-08-19 +date: 2022-08-23 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 4c021eb672f6f..5fdbb7cc87496 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: 2022-08-19 +date: 2022-08-23 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 8db4a7f5be4af..42135ba446264 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: 2022-08-19 +date: 2022-08-23 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 2031515366b45..4fabd8c35cf77 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: 2022-08-19 +date: 2022-08-23 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 d531ddac9c93a..a4f8c8e997a9a 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: 2022-08-19 +date: 2022-08-23 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 c0c4ec1f78d0e..3f0f7d6140f6b 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-jest-helpers'] --- import kbnTestJestHelpersObj from './kbn_test_jest_helpers.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index aa538bb2de0ac..a8db1640fcadd 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_type_summarizer.mdx b/api_docs/kbn_type_summarizer.mdx index 64a788dd4d0da..700c492b13237 100644 --- a/api_docs/kbn_type_summarizer.mdx +++ b/api_docs/kbn_type_summarizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer title: "@kbn/type-summarizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/type-summarizer plugin -date: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer'] --- import kbnTypeSummarizerObj from './kbn_type_summarizer.devdocs.json'; diff --git a/api_docs/kbn_type_summarizer_core.mdx b/api_docs/kbn_type_summarizer_core.mdx index 787bd36ac15a0..43650aa4d96ca 100644 --- a/api_docs/kbn_type_summarizer_core.mdx +++ b/api_docs/kbn_type_summarizer_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer-core title: "@kbn/type-summarizer-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/type-summarizer-core plugin -date: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer-core'] --- import kbnTypeSummarizerCoreObj from './kbn_type_summarizer_core.devdocs.json'; diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index fe5fa6c777a8b..bc6d65bdfba02 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: 2022-08-19 +date: 2022-08-23 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_theme.mdx b/api_docs/kbn_ui_theme.mdx index ef7d91462b8b2..940ac831af7d9 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: 2022-08-19 +date: 2022-08-23 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 d0cd8d617579a..7f94cb018758b 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: 2022-08-19 +date: 2022-08-23 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 7444454975d1f..ca332bd0fc7ba 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: 2022-08-19 +date: 2022-08-23 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 ca936d8e85829..40d7aac8f7431 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: 2022-08-19 +date: 2022-08-23 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 b5f6c64fe7d40..40ff0c662fc01 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: 2022-08-19 +date: 2022-08-23 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 dfc16a868b76c..09302217ea0e0 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: 2022-08-19 +date: 2022-08-23 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 81dd0a92aae58..35a7f556afab9 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: 2022-08-19 +date: 2022-08-23 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 8a3256bfe6356..8adb1ee646c47 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: 2022-08-19 +date: 2022-08-23 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 0338c4acbd1a0..5f4ebd51a0e09 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: 2022-08-19 +date: 2022-08-23 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 d9c8ac5b13da4..94f4e73a61f44 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: 2022-08-19 +date: 2022-08-23 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 087be9506146a..ffe68cbbc5ec4 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: 2022-08-19 +date: 2022-08-23 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 2c6cf1f856933..28fa8e87c67cb 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: 2022-08-19 +date: 2022-08-23 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 db4da6da5b43b..78cab4aef2d92 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: 2022-08-19 +date: 2022-08-23 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 025462706a8b1..590e82da3c93d 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: 2022-08-19 +date: 2022-08-23 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 8b6d35e582174..08a667c54dec7 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: 2022-08-19 +date: 2022-08-23 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 21bc031684c3a..2f9de9d95d16f 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: 2022-08-19 +date: 2022-08-23 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 340f7e4fe16a3..e03da0c3c940f 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: 2022-08-19 +date: 2022-08-23 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 1ed12e9ad857c..192e22f20fbce 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: 2022-08-19 +date: 2022-08-23 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 2efb2bd603b5f..ae4b8f4ee0c4f 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: 2022-08-19 +date: 2022-08-23 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 912e503c45625..cf70a077351d7 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: 2022-08-19 +date: 2022-08-23 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 2227c93f3d5e4..6fb26b79b8264 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: 2022-08-19 +date: 2022-08-23 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 faa8dd1e5b35c..6699cae40f621 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: 2022-08-19 +date: 2022-08-23 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 2dda5b0b46d3c..2f3dad4987357 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/observability.devdocs.json b/api_docs/observability.devdocs.json index 048601d55003f..704fcd389865d 100644 --- a/api_docs/observability.devdocs.json +++ b/api_docs/observability.devdocs.json @@ -696,9 +696,9 @@ }, " | undefined; list: () => string[]; }; selectedAlertId?: string | undefined; } & ", "CommonProps", - " & { as?: \"div\" | undefined; } & _EuiFlyoutProps & Omit, HTMLDivElement>, keyof _EuiFlyoutProps> & Omit, HTMLDivElement>, \"key\" | \"css\" | keyof React.HTMLAttributes> & { ref?: React.RefObject | ((instance: HTMLDivElement | null) => void) | null | undefined; }, \"children\" | \"onError\" | \"hidden\" | \"color\" | \"title\" | \"onChange\" | \"onKeyDown\" | \"onClick\" | \"id\" | \"security\" | \"defaultValue\" | \"lang\" | \"defaultChecked\" | \"suppressContentEditableWarning\" | \"suppressHydrationWarning\" | \"accessKey\" | \"contentEditable\" | \"contextMenu\" | \"dir\" | \"draggable\" | \"placeholder\" | \"slot\" | \"spellCheck\" | \"tabIndex\" | \"translate\" | \"radioGroup\" | \"about\" | \"datatype\" | \"inlist\" | \"prefix\" | \"property\" | \"resource\" | \"typeof\" | \"vocab\" | \"autoCapitalize\" | \"autoCorrect\" | \"autoSave\" | \"itemProp\" | \"itemScope\" | \"itemType\" | \"itemID\" | \"itemRef\" | \"results\" | \"unselectable\" | \"inputMode\" | \"is\" | \"aria-activedescendant\" | \"aria-atomic\" | \"aria-autocomplete\" | \"aria-busy\" | \"aria-checked\" | \"aria-colcount\" | \"aria-colindex\" | \"aria-colspan\" | \"aria-controls\" | \"aria-current\" | \"aria-describedby\" | \"aria-details\" | \"aria-disabled\" | \"aria-dropeffect\" | \"aria-errormessage\" | \"aria-expanded\" | \"aria-flowto\" | \"aria-grabbed\" | \"aria-haspopup\" | \"aria-hidden\" | \"aria-invalid\" | \"aria-keyshortcuts\" | \"aria-labelledby\" | \"aria-level\" | \"aria-live\" | \"aria-modal\" | \"aria-multiline\" | \"aria-multiselectable\" | \"aria-orientation\" | \"aria-owns\" | \"aria-placeholder\" | \"aria-posinset\" | \"aria-pressed\" | \"aria-readonly\" | \"aria-relevant\" | \"aria-required\" | \"aria-roledescription\" | \"aria-rowcount\" | \"aria-rowindex\" | \"aria-rowspan\" | \"aria-selected\" | \"aria-setsize\" | \"aria-sort\" | \"aria-valuemax\" | \"aria-valuemin\" | \"aria-valuenow\" | \"aria-valuetext\" | \"dangerouslySetInnerHTML\" | \"onCopy\" | \"onCopyCapture\" | \"onCut\" | \"onCutCapture\" | \"onPaste\" | \"onPasteCapture\" | \"onCompositionEnd\" | \"onCompositionEndCapture\" | \"onCompositionStart\" | \"onCompositionStartCapture\" | \"onCompositionUpdate\" | \"onCompositionUpdateCapture\" | \"onFocus\" | \"onFocusCapture\" | \"onBlur\" | \"onBlurCapture\" | \"onChangeCapture\" | \"onBeforeInput\" | \"onBeforeInputCapture\" | \"onInput\" | \"onInputCapture\" | \"onReset\" | \"onResetCapture\" | \"onSubmit\" | \"onSubmitCapture\" | \"onInvalid\" | \"onInvalidCapture\" | \"onLoad\" | \"onLoadCapture\" | \"onErrorCapture\" | \"onKeyDownCapture\" | \"onKeyPress\" | \"onKeyPressCapture\" | \"onKeyUp\" | \"onKeyUpCapture\" | \"onAbort\" | \"onAbortCapture\" | \"onCanPlay\" | \"onCanPlayCapture\" | \"onCanPlayThrough\" | \"onCanPlayThroughCapture\" | \"onDurationChange\" | \"onDurationChangeCapture\" | \"onEmptied\" | \"onEmptiedCapture\" | \"onEncrypted\" | \"onEncryptedCapture\" | \"onEnded\" | \"onEndedCapture\" | \"onLoadedData\" | \"onLoadedDataCapture\" | \"onLoadedMetadata\" | \"onLoadedMetadataCapture\" | \"onLoadStart\" | \"onLoadStartCapture\" | \"onPause\" | \"onPauseCapture\" | \"onPlay\" | \"onPlayCapture\" | \"onPlaying\" | \"onPlayingCapture\" | \"onProgress\" | \"onProgressCapture\" | \"onRateChange\" | \"onRateChangeCapture\" | \"onSeeked\" | \"onSeekedCapture\" | \"onSeeking\" | \"onSeekingCapture\" | \"onStalled\" | \"onStalledCapture\" | \"onSuspend\" | \"onSuspendCapture\" | \"onTimeUpdate\" | \"onTimeUpdateCapture\" | \"onVolumeChange\" | \"onVolumeChangeCapture\" | \"onWaiting\" | \"onWaitingCapture\" | \"onAuxClick\" | \"onAuxClickCapture\" | \"onClickCapture\" | \"onContextMenu\" | \"onContextMenuCapture\" | \"onDoubleClick\" | \"onDoubleClickCapture\" | \"onDrag\" | \"onDragCapture\" | \"onDragEnd\" | \"onDragEndCapture\" | \"onDragEnter\" | \"onDragEnterCapture\" | \"onDragExit\" | \"onDragExitCapture\" | \"onDragLeave\" | \"onDragLeaveCapture\" | \"onDragOver\" | \"onDragOverCapture\" | \"onDragStart\" | \"onDragStartCapture\" | \"onDrop\" | \"onDropCapture\" | \"onMouseDown\" | \"onMouseDownCapture\" | \"onMouseEnter\" | \"onMouseLeave\" | \"onMouseMove\" | \"onMouseMoveCapture\" | \"onMouseOut\" | \"onMouseOutCapture\" | \"onMouseOver\" | \"onMouseOverCapture\" | \"onMouseUp\" | \"onMouseUpCapture\" | \"onSelect\" | \"onSelectCapture\" | \"onTouchCancel\" | \"onTouchCancelCapture\" | \"onTouchEnd\" | \"onTouchEndCapture\" | \"onTouchMove\" | \"onTouchMoveCapture\" | \"onTouchStart\" | \"onTouchStartCapture\" | \"onPointerDown\" | \"onPointerDownCapture\" | \"onPointerMove\" | \"onPointerMoveCapture\" | \"onPointerUp\" | \"onPointerUpCapture\" | \"onPointerCancel\" | \"onPointerCancelCapture\" | \"onPointerEnter\" | \"onPointerEnterCapture\" | \"onPointerLeave\" | \"onPointerLeaveCapture\" | \"onPointerOver\" | \"onPointerOverCapture\" | \"onPointerOut\" | \"onPointerOutCapture\" | \"onGotPointerCapture\" | \"onGotPointerCaptureCapture\" | \"onLostPointerCapture\" | \"onLostPointerCaptureCapture\" | \"onScroll\" | \"onScrollCapture\" | \"onWheel\" | \"onWheelCapture\" | \"onAnimationStart\" | \"onAnimationStartCapture\" | \"onAnimationEnd\" | \"onAnimationEndCapture\" | \"onAnimationIteration\" | \"onAnimationIterationCapture\" | \"onTransitionEnd\" | \"onTransitionEndCapture\" | \"as\" | keyof ", + " & { as?: \"div\" | undefined; } & _EuiFlyoutProps & Omit, HTMLDivElement>, keyof _EuiFlyoutProps> & Omit, HTMLDivElement>, \"key\" | keyof React.HTMLAttributes | \"css\"> & { ref?: React.RefObject | ((instance: HTMLDivElement | null) => void) | null | undefined; }, \"children\" | \"onError\" | \"hidden\" | \"color\" | \"title\" | \"onChange\" | \"onKeyDown\" | \"onClick\" | \"id\" | \"security\" | \"defaultValue\" | \"lang\" | \"defaultChecked\" | \"suppressContentEditableWarning\" | \"suppressHydrationWarning\" | \"accessKey\" | \"contentEditable\" | \"contextMenu\" | \"dir\" | \"draggable\" | \"placeholder\" | \"slot\" | \"spellCheck\" | \"tabIndex\" | \"translate\" | \"radioGroup\" | \"about\" | \"datatype\" | \"inlist\" | \"prefix\" | \"property\" | \"resource\" | \"typeof\" | \"vocab\" | \"autoCapitalize\" | \"autoCorrect\" | \"autoSave\" | \"itemProp\" | \"itemScope\" | \"itemType\" | \"itemID\" | \"itemRef\" | \"results\" | \"unselectable\" | \"inputMode\" | \"is\" | \"aria-activedescendant\" | \"aria-atomic\" | \"aria-autocomplete\" | \"aria-busy\" | \"aria-checked\" | \"aria-colcount\" | \"aria-colindex\" | \"aria-colspan\" | \"aria-controls\" | \"aria-current\" | \"aria-describedby\" | \"aria-details\" | \"aria-disabled\" | \"aria-dropeffect\" | \"aria-errormessage\" | \"aria-expanded\" | \"aria-flowto\" | \"aria-grabbed\" | \"aria-haspopup\" | \"aria-hidden\" | \"aria-invalid\" | \"aria-keyshortcuts\" | \"aria-labelledby\" | \"aria-level\" | \"aria-live\" | \"aria-modal\" | \"aria-multiline\" | \"aria-multiselectable\" | \"aria-orientation\" | \"aria-owns\" | \"aria-placeholder\" | \"aria-posinset\" | \"aria-pressed\" | \"aria-readonly\" | \"aria-relevant\" | \"aria-required\" | \"aria-roledescription\" | \"aria-rowcount\" | \"aria-rowindex\" | \"aria-rowspan\" | \"aria-selected\" | \"aria-setsize\" | \"aria-sort\" | \"aria-valuemax\" | \"aria-valuemin\" | \"aria-valuenow\" | \"aria-valuetext\" | \"dangerouslySetInnerHTML\" | \"onCopy\" | \"onCopyCapture\" | \"onCut\" | \"onCutCapture\" | \"onPaste\" | \"onPasteCapture\" | \"onCompositionEnd\" | \"onCompositionEndCapture\" | \"onCompositionStart\" | \"onCompositionStartCapture\" | \"onCompositionUpdate\" | \"onCompositionUpdateCapture\" | \"onFocus\" | \"onFocusCapture\" | \"onBlur\" | \"onBlurCapture\" | \"onChangeCapture\" | \"onBeforeInput\" | \"onBeforeInputCapture\" | \"onInput\" | \"onInputCapture\" | \"onReset\" | \"onResetCapture\" | \"onSubmit\" | \"onSubmitCapture\" | \"onInvalid\" | \"onInvalidCapture\" | \"onLoad\" | \"onLoadCapture\" | \"onErrorCapture\" | \"onKeyDownCapture\" | \"onKeyPress\" | \"onKeyPressCapture\" | \"onKeyUp\" | \"onKeyUpCapture\" | \"onAbort\" | \"onAbortCapture\" | \"onCanPlay\" | \"onCanPlayCapture\" | \"onCanPlayThrough\" | \"onCanPlayThroughCapture\" | \"onDurationChange\" | \"onDurationChangeCapture\" | \"onEmptied\" | \"onEmptiedCapture\" | \"onEncrypted\" | \"onEncryptedCapture\" | \"onEnded\" | \"onEndedCapture\" | \"onLoadedData\" | \"onLoadedDataCapture\" | \"onLoadedMetadata\" | \"onLoadedMetadataCapture\" | \"onLoadStart\" | \"onLoadStartCapture\" | \"onPause\" | \"onPauseCapture\" | \"onPlay\" | \"onPlayCapture\" | \"onPlaying\" | \"onPlayingCapture\" | \"onProgress\" | \"onProgressCapture\" | \"onRateChange\" | \"onRateChangeCapture\" | \"onSeeked\" | \"onSeekedCapture\" | \"onSeeking\" | \"onSeekingCapture\" | \"onStalled\" | \"onStalledCapture\" | \"onSuspend\" | \"onSuspendCapture\" | \"onTimeUpdate\" | \"onTimeUpdateCapture\" | \"onVolumeChange\" | \"onVolumeChangeCapture\" | \"onWaiting\" | \"onWaitingCapture\" | \"onAuxClick\" | \"onAuxClickCapture\" | \"onClickCapture\" | \"onContextMenu\" | \"onContextMenuCapture\" | \"onDoubleClick\" | \"onDoubleClickCapture\" | \"onDrag\" | \"onDragCapture\" | \"onDragEnd\" | \"onDragEndCapture\" | \"onDragEnter\" | \"onDragEnterCapture\" | \"onDragExit\" | \"onDragExitCapture\" | \"onDragLeave\" | \"onDragLeaveCapture\" | \"onDragOver\" | \"onDragOverCapture\" | \"onDragStart\" | \"onDragStartCapture\" | \"onDrop\" | \"onDropCapture\" | \"onMouseDown\" | \"onMouseDownCapture\" | \"onMouseEnter\" | \"onMouseLeave\" | \"onMouseMove\" | \"onMouseMoveCapture\" | \"onMouseOut\" | \"onMouseOutCapture\" | \"onMouseOver\" | \"onMouseOverCapture\" | \"onMouseUp\" | \"onMouseUpCapture\" | \"onSelect\" | \"onSelectCapture\" | \"onTouchCancel\" | \"onTouchCancelCapture\" | \"onTouchEnd\" | \"onTouchEndCapture\" | \"onTouchMove\" | \"onTouchMoveCapture\" | \"onTouchStart\" | \"onTouchStartCapture\" | \"onPointerDown\" | \"onPointerDownCapture\" | \"onPointerMove\" | \"onPointerMoveCapture\" | \"onPointerUp\" | \"onPointerUpCapture\" | \"onPointerCancel\" | \"onPointerCancelCapture\" | \"onPointerEnter\" | \"onPointerEnterCapture\" | \"onPointerLeave\" | \"onPointerLeaveCapture\" | \"onPointerOver\" | \"onPointerOverCapture\" | \"onPointerOut\" | \"onPointerOutCapture\" | \"onGotPointerCapture\" | \"onGotPointerCaptureCapture\" | \"onLostPointerCapture\" | \"onLostPointerCaptureCapture\" | \"onScroll\" | \"onScrollCapture\" | \"onWheel\" | \"onWheelCapture\" | \"onAnimationStart\" | \"onAnimationStartCapture\" | \"onAnimationEnd\" | \"onAnimationEndCapture\" | \"onAnimationIteration\" | \"onAnimationIterationCapture\" | \"onTransitionEnd\" | \"onTransitionEndCapture\" | \"as\" | keyof ", "CommonProps", - " | keyof React.ClassAttributes | keyof _EuiFlyoutProps>, \"children\" | \"onError\" | \"hidden\" | \"color\" | \"alert\" | \"title\" | \"onChange\" | \"onKeyDown\" | \"onClick\" | \"id\" | \"key\" | \"css\" | \"security\" | \"defaultValue\" | \"lang\" | \"defaultChecked\" | \"suppressContentEditableWarning\" | \"suppressHydrationWarning\" | \"accessKey\" | \"contentEditable\" | \"contextMenu\" | \"dir\" | \"draggable\" | \"placeholder\" | \"slot\" | \"spellCheck\" | \"tabIndex\" | \"translate\" | \"radioGroup\" | \"about\" | \"datatype\" | \"inlist\" | \"prefix\" | \"property\" | \"resource\" | \"typeof\" | \"vocab\" | \"autoCapitalize\" | \"autoCorrect\" | \"autoSave\" | \"itemProp\" | \"itemScope\" | \"itemType\" | \"itemID\" | \"itemRef\" | \"results\" | \"unselectable\" | \"inputMode\" | \"is\" | \"aria-activedescendant\" | \"aria-atomic\" | \"aria-autocomplete\" | \"aria-busy\" | \"aria-checked\" | \"aria-colcount\" | \"aria-colindex\" | \"aria-colspan\" | \"aria-controls\" | \"aria-current\" | \"aria-describedby\" | \"aria-details\" | \"aria-disabled\" | \"aria-dropeffect\" | \"aria-errormessage\" | \"aria-expanded\" | \"aria-flowto\" | \"aria-grabbed\" | \"aria-haspopup\" | \"aria-hidden\" | \"aria-invalid\" | \"aria-keyshortcuts\" | \"aria-labelledby\" | \"aria-level\" | \"aria-live\" | \"aria-modal\" | \"aria-multiline\" | \"aria-multiselectable\" | \"aria-orientation\" | \"aria-owns\" | \"aria-placeholder\" | \"aria-posinset\" | \"aria-pressed\" | \"aria-readonly\" | \"aria-relevant\" | \"aria-required\" | \"aria-roledescription\" | \"aria-rowcount\" | \"aria-rowindex\" | \"aria-rowspan\" | \"aria-selected\" | \"aria-setsize\" | \"aria-sort\" | \"aria-valuemax\" | \"aria-valuemin\" | \"aria-valuenow\" | \"aria-valuetext\" | \"dangerouslySetInnerHTML\" | \"onCopy\" | \"onCopyCapture\" | \"onCut\" | \"onCutCapture\" | \"onPaste\" | \"onPasteCapture\" | \"onCompositionEnd\" | \"onCompositionEndCapture\" | \"onCompositionStart\" | \"onCompositionStartCapture\" | \"onCompositionUpdate\" | \"onCompositionUpdateCapture\" | \"onFocus\" | \"onFocusCapture\" | \"onBlur\" | \"onBlurCapture\" | \"onChangeCapture\" | \"onBeforeInput\" | \"onBeforeInputCapture\" | \"onInput\" | \"onInputCapture\" | \"onReset\" | \"onResetCapture\" | \"onSubmit\" | \"onSubmitCapture\" | \"onInvalid\" | \"onInvalidCapture\" | \"onLoad\" | \"onLoadCapture\" | \"onErrorCapture\" | \"onKeyDownCapture\" | \"onKeyPress\" | \"onKeyPressCapture\" | \"onKeyUp\" | \"onKeyUpCapture\" | \"onAbort\" | \"onAbortCapture\" | \"onCanPlay\" | \"onCanPlayCapture\" | \"onCanPlayThrough\" | \"onCanPlayThroughCapture\" | \"onDurationChange\" | \"onDurationChangeCapture\" | \"onEmptied\" | \"onEmptiedCapture\" | \"onEncrypted\" | \"onEncryptedCapture\" | \"onEnded\" | \"onEndedCapture\" | \"onLoadedData\" | \"onLoadedDataCapture\" | \"onLoadedMetadata\" | \"onLoadedMetadataCapture\" | \"onLoadStart\" | \"onLoadStartCapture\" | \"onPause\" | \"onPauseCapture\" | \"onPlay\" | \"onPlayCapture\" | \"onPlaying\" | \"onPlayingCapture\" | \"onProgress\" | \"onProgressCapture\" | \"onRateChange\" | \"onRateChangeCapture\" | \"onSeeked\" | \"onSeekedCapture\" | \"onSeeking\" | \"onSeekingCapture\" | \"onStalled\" | \"onStalledCapture\" | \"onSuspend\" | \"onSuspendCapture\" | \"onTimeUpdate\" | \"onTimeUpdateCapture\" | \"onVolumeChange\" | \"onVolumeChangeCapture\" | \"onWaiting\" | \"onWaitingCapture\" | \"onAuxClick\" | \"onAuxClickCapture\" | \"onClickCapture\" | \"onContextMenu\" | \"onContextMenuCapture\" | \"onDoubleClick\" | \"onDoubleClickCapture\" | \"onDrag\" | \"onDragCapture\" | \"onDragEnd\" | \"onDragEndCapture\" | \"onDragEnter\" | \"onDragEnterCapture\" | \"onDragExit\" | \"onDragExitCapture\" | \"onDragLeave\" | \"onDragLeaveCapture\" | \"onDragOver\" | \"onDragOverCapture\" | \"onDragStart\" | \"onDragStartCapture\" | \"onDrop\" | \"onDropCapture\" | \"onMouseDown\" | \"onMouseDownCapture\" | \"onMouseEnter\" | \"onMouseLeave\" | \"onMouseMove\" | \"onMouseMoveCapture\" | \"onMouseOut\" | \"onMouseOutCapture\" | \"onMouseOver\" | \"onMouseOverCapture\" | \"onMouseUp\" | \"onMouseUpCapture\" | \"onSelect\" | \"onSelectCapture\" | \"onTouchCancel\" | \"onTouchCancelCapture\" | \"onTouchEnd\" | \"onTouchEndCapture\" | \"onTouchMove\" | \"onTouchMoveCapture\" | \"onTouchStart\" | \"onTouchStartCapture\" | \"onPointerDown\" | \"onPointerDownCapture\" | \"onPointerMove\" | \"onPointerMoveCapture\" | \"onPointerUp\" | \"onPointerUpCapture\" | \"onPointerCancel\" | \"onPointerCancelCapture\" | \"onPointerEnter\" | \"onPointerEnterCapture\" | \"onPointerLeave\" | \"onPointerLeaveCapture\" | \"onPointerOver\" | \"onPointerOverCapture\" | \"onPointerOut\" | \"onPointerOutCapture\" | \"onGotPointerCapture\" | \"onGotPointerCaptureCapture\" | \"onLostPointerCapture\" | \"onLostPointerCaptureCapture\" | \"onScroll\" | \"onScrollCapture\" | \"onWheel\" | \"onWheelCapture\" | \"onAnimationStart\" | \"onAnimationStartCapture\" | \"onAnimationEnd\" | \"onAnimationEndCapture\" | \"onAnimationIteration\" | \"onAnimationIterationCapture\" | \"onTransitionEnd\" | \"onTransitionEndCapture\" | \"as\" | keyof ", + " | keyof React.ClassAttributes | keyof _EuiFlyoutProps>, \"children\" | \"onError\" | \"hidden\" | \"color\" | \"alert\" | \"title\" | \"onChange\" | \"onKeyDown\" | \"onClick\" | \"id\" | \"security\" | \"key\" | \"defaultValue\" | \"lang\" | \"defaultChecked\" | \"suppressContentEditableWarning\" | \"suppressHydrationWarning\" | \"accessKey\" | \"contentEditable\" | \"contextMenu\" | \"dir\" | \"draggable\" | \"placeholder\" | \"slot\" | \"spellCheck\" | \"tabIndex\" | \"translate\" | \"radioGroup\" | \"about\" | \"datatype\" | \"inlist\" | \"prefix\" | \"property\" | \"resource\" | \"typeof\" | \"vocab\" | \"autoCapitalize\" | \"autoCorrect\" | \"autoSave\" | \"itemProp\" | \"itemScope\" | \"itemType\" | \"itemID\" | \"itemRef\" | \"results\" | \"unselectable\" | \"inputMode\" | \"is\" | \"aria-activedescendant\" | \"aria-atomic\" | \"aria-autocomplete\" | \"aria-busy\" | \"aria-checked\" | \"aria-colcount\" | \"aria-colindex\" | \"aria-colspan\" | \"aria-controls\" | \"aria-current\" | \"aria-describedby\" | \"aria-details\" | \"aria-disabled\" | \"aria-dropeffect\" | \"aria-errormessage\" | \"aria-expanded\" | \"aria-flowto\" | \"aria-grabbed\" | \"aria-haspopup\" | \"aria-hidden\" | \"aria-invalid\" | \"aria-keyshortcuts\" | \"aria-labelledby\" | \"aria-level\" | \"aria-live\" | \"aria-modal\" | \"aria-multiline\" | \"aria-multiselectable\" | \"aria-orientation\" | \"aria-owns\" | \"aria-placeholder\" | \"aria-posinset\" | \"aria-pressed\" | \"aria-readonly\" | \"aria-relevant\" | \"aria-required\" | \"aria-roledescription\" | \"aria-rowcount\" | \"aria-rowindex\" | \"aria-rowspan\" | \"aria-selected\" | \"aria-setsize\" | \"aria-sort\" | \"aria-valuemax\" | \"aria-valuemin\" | \"aria-valuenow\" | \"aria-valuetext\" | \"dangerouslySetInnerHTML\" | \"onCopy\" | \"onCopyCapture\" | \"onCut\" | \"onCutCapture\" | \"onPaste\" | \"onPasteCapture\" | \"onCompositionEnd\" | \"onCompositionEndCapture\" | \"onCompositionStart\" | \"onCompositionStartCapture\" | \"onCompositionUpdate\" | \"onCompositionUpdateCapture\" | \"onFocus\" | \"onFocusCapture\" | \"onBlur\" | \"onBlurCapture\" | \"onChangeCapture\" | \"onBeforeInput\" | \"onBeforeInputCapture\" | \"onInput\" | \"onInputCapture\" | \"onReset\" | \"onResetCapture\" | \"onSubmit\" | \"onSubmitCapture\" | \"onInvalid\" | \"onInvalidCapture\" | \"onLoad\" | \"onLoadCapture\" | \"onErrorCapture\" | \"onKeyDownCapture\" | \"onKeyPress\" | \"onKeyPressCapture\" | \"onKeyUp\" | \"onKeyUpCapture\" | \"onAbort\" | \"onAbortCapture\" | \"onCanPlay\" | \"onCanPlayCapture\" | \"onCanPlayThrough\" | \"onCanPlayThroughCapture\" | \"onDurationChange\" | \"onDurationChangeCapture\" | \"onEmptied\" | \"onEmptiedCapture\" | \"onEncrypted\" | \"onEncryptedCapture\" | \"onEnded\" | \"onEndedCapture\" | \"onLoadedData\" | \"onLoadedDataCapture\" | \"onLoadedMetadata\" | \"onLoadedMetadataCapture\" | \"onLoadStart\" | \"onLoadStartCapture\" | \"onPause\" | \"onPauseCapture\" | \"onPlay\" | \"onPlayCapture\" | \"onPlaying\" | \"onPlayingCapture\" | \"onProgress\" | \"onProgressCapture\" | \"onRateChange\" | \"onRateChangeCapture\" | \"onSeeked\" | \"onSeekedCapture\" | \"onSeeking\" | \"onSeekingCapture\" | \"onStalled\" | \"onStalledCapture\" | \"onSuspend\" | \"onSuspendCapture\" | \"onTimeUpdate\" | \"onTimeUpdateCapture\" | \"onVolumeChange\" | \"onVolumeChangeCapture\" | \"onWaiting\" | \"onWaitingCapture\" | \"onAuxClick\" | \"onAuxClickCapture\" | \"onClickCapture\" | \"onContextMenu\" | \"onContextMenuCapture\" | \"onDoubleClick\" | \"onDoubleClickCapture\" | \"onDrag\" | \"onDragCapture\" | \"onDragEnd\" | \"onDragEndCapture\" | \"onDragEnter\" | \"onDragEnterCapture\" | \"onDragExit\" | \"onDragExitCapture\" | \"onDragLeave\" | \"onDragLeaveCapture\" | \"onDragOver\" | \"onDragOverCapture\" | \"onDragStart\" | \"onDragStartCapture\" | \"onDrop\" | \"onDropCapture\" | \"onMouseDown\" | \"onMouseDownCapture\" | \"onMouseEnter\" | \"onMouseLeave\" | \"onMouseMove\" | \"onMouseMoveCapture\" | \"onMouseOut\" | \"onMouseOutCapture\" | \"onMouseOver\" | \"onMouseOverCapture\" | \"onMouseUp\" | \"onMouseUpCapture\" | \"onSelect\" | \"onSelectCapture\" | \"onTouchCancel\" | \"onTouchCancelCapture\" | \"onTouchEnd\" | \"onTouchEndCapture\" | \"onTouchMove\" | \"onTouchMoveCapture\" | \"onTouchStart\" | \"onTouchStartCapture\" | \"onPointerDown\" | \"onPointerDownCapture\" | \"onPointerMove\" | \"onPointerMoveCapture\" | \"onPointerUp\" | \"onPointerUpCapture\" | \"onPointerCancel\" | \"onPointerCancelCapture\" | \"onPointerEnter\" | \"onPointerEnterCapture\" | \"onPointerLeave\" | \"onPointerLeaveCapture\" | \"onPointerOver\" | \"onPointerOverCapture\" | \"onPointerOut\" | \"onPointerOutCapture\" | \"onGotPointerCapture\" | \"onGotPointerCaptureCapture\" | \"onLostPointerCapture\" | \"onLostPointerCaptureCapture\" | \"onScroll\" | \"onScrollCapture\" | \"onWheel\" | \"onWheelCapture\" | \"onAnimationStart\" | \"onAnimationStartCapture\" | \"onAnimationEnd\" | \"onAnimationEndCapture\" | \"onAnimationIteration\" | \"onAnimationIterationCapture\" | \"onTransitionEnd\" | \"onTransitionEndCapture\" | \"as\" | \"css\" | keyof ", "CommonProps", " | \"alerts\" | keyof _EuiFlyoutProps | \"isInApp\" | \"observabilityRuleTypeRegistry\" | \"selectedAlertId\"> & { ref?: React.RefObject | ((instance: HTMLDivElement | null) => void) | null | undefined; }> & { readonly _result: ({ alert, alerts, isInApp, observabilityRuleTypeRegistry, onClose, selectedAlertId, }: AlertsFlyoutProps) => JSX.Element | null; }" ], diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 4633926b17a14..0ba96bc264095 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: 2022-08-19 +date: 2022-08-23 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 600c916fddbf2..d2ba0e10c241d 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: 2022-08-19 +date: 2022-08-23 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 9f4dc17eeec15..f0566f96ebf28 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -15,13 +15,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Count | Plugins or Packages with a
public API | Number of teams | |--------------|----------|------------------------| -| 410 | 340 | 36 | +| 413 | 343 | 36 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 29330 | 180 | 19815 | 917 | +| 29473 | 180 | 19859 | 918 | ## Plugin Directory @@ -40,13 +40,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 29 | 0 | 24 | 0 | | | [Cloud Security Posture](https://github.com/orgs/elastic/teams/cloud-posture-security) | The cloud security posture plugin | 18 | 0 | 2 | 3 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 13 | 0 | 13 | 1 | -| | [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 | 207 | 0 | 199 | 7 | -| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2524 | 2 | 296 | 6 | +| | [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 | 206 | 0 | 198 | 7 | +| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2524 | 1 | 216 | 5 | | crossClusterReplication | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | | | [Fleet](https://github.com/orgs/elastic/teams/fleet) | Add custom data integrations so they can be displayed in the Fleet integrations app | 102 | 0 | 83 | 1 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds the Dashboard app to Kibana | 146 | 0 | 141 | 12 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 52 | 0 | 51 | 0 | -| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 3114 | 34 | 2428 | 22 | +| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 3117 | 34 | 2431 | 22 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | This plugin provides the ability to create data views via a modal flyout inside Kibana apps | 15 | 0 | 7 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Reusable data view field editor across Kibana | 49 | 0 | 29 | 3 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Data view management app | 2 | 0 | 2 | 0 | @@ -143,7 +143,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 31 | 0 | 26 | 6 | | | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 1 | 0 | 1 | 0 | | | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 11 | 0 | 10 | 0 | -| | [Protections Experience Team](https://github.com/orgs/elastic/teams/protections-experience) | Elastic threat intelligence helps you see if you are open to or have been subject to current or historical known threats | 15 | 0 | 3 | 1 | +| | [Protections Experience Team](https://github.com/orgs/elastic/teams/protections-experience) | Elastic threat intelligence helps you see if you are open to or have been subject to current or historical known threats | 16 | 0 | 4 | 2 | | | [Security solution](https://github.com/orgs/elastic/teams/security-solution) | - | 447 | 1 | 342 | 32 | | | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the transforms features provided by Elastic. Transforms enable you to convert existing Elasticsearch indices into summarized indices, which provide opportunities for new insights and analytics. | 4 | 0 | 4 | 1 | | translations | [Kibana Localization](https://github.com/orgs/elastic/teams/kibana-localization) | - | 0 | 0 | 0 | 0 | @@ -169,7 +169,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Registers the vega visualization. Is the elastic version of vega and vega-lite libraries. | 2 | 0 | 2 | 0 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the vislib visualizations. These are the classical area/line/bar, pie, gauge/goal and heatmap charts. We want to replace them with elastic-charts. | 26 | 0 | 25 | 1 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the new xy-axis chart using the elastic-charts library, which will eventually replace the vislib xy-axis charts including bar, area, and line. | 53 | 0 | 50 | 5 | -| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the shared architecture among all the legacy visualizations, e.g. the visualization type registry or the visualization embeddable. | 414 | 12 | 386 | 15 | +| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the shared architecture among all the legacy visualizations, e.g. the visualization type registry or the visualization embeddable. | 416 | 12 | 388 | 15 | | watcher | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | ## Package Directory @@ -277,11 +277,14 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | Kibana Core | - | 6 | 0 | 6 | 0 | | | Kibana Core | - | 94 | 1 | 66 | 0 | | | Kibana Core | - | 288 | 1 | 125 | 0 | +| | [Owner missing] | - | 35 | 0 | 29 | 1 | +| | [Owner missing] | - | 4 | 0 | 4 | 0 | | | Kibana Core | - | 2 | 0 | 1 | 0 | | | Kibana Core | - | 6 | 0 | 6 | 0 | | | Kibana Core | - | 7 | 0 | 7 | 0 | | | Kibana Core | - | 82 | 0 | 41 | 0 | | | Kibana Core | - | 225 | 0 | 82 | 0 | +| | [Owner missing] | - | 99 | 1 | 86 | 0 | | | Kibana Core | - | 11 | 0 | 9 | 0 | | | Kibana Core | - | 5 | 0 | 5 | 0 | | | Kibana Core | - | 6 | 0 | 4 | 0 | diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index 2ce0520e316c8..3f66038b74b58 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] --- import presentationUtilObj from './presentation_util.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index b66f92ee13bcd..2a656809e94f3 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: 2022-08-19 +date: 2022-08-23 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 42db5fd765a35..7d157d3815ceb 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: 2022-08-19 +date: 2022-08-23 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 b1f6fd70a78a1..a44c6dd8737a9 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: 2022-08-19 +date: 2022-08-23 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 7040ad0cddc61..2730111e1f844 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: 2022-08-19 +date: 2022-08-23 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 f96ee42b015ac..3df69f3cb6180 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: 2022-08-19 +date: 2022-08-23 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 eff783c005c24..6b3bd8a5df41a 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjects'] --- import savedObjectsObj from './saved_objects.devdocs.json'; diff --git a/api_docs/saved_objects_management.devdocs.json b/api_docs/saved_objects_management.devdocs.json index a0876d761e9a5..ad24e52369344 100644 --- a/api_docs/saved_objects_management.devdocs.json +++ b/api_docs/saved_objects_management.devdocs.json @@ -294,7 +294,7 @@ "section": "def-public.SavedObjectsManagementRecord", "text": "SavedObjectsManagementRecord" }, - "; defaultValue?: string | number | readonly string[] | undefined; lang?: string | undefined; defaultChecked?: boolean | undefined; suppressContentEditableWarning?: boolean | undefined; suppressHydrationWarning?: boolean | undefined; accessKey?: string | undefined; contentEditable?: \"inherit\" | Booleanish | undefined; contextMenu?: string | undefined; dir?: string | undefined; draggable?: Booleanish | undefined; placeholder?: string | undefined; slot?: string | undefined; spellCheck?: Booleanish | undefined; style?: React.CSSProperties | undefined; tabIndex?: number | undefined; translate?: \"no\" | \"yes\" | undefined; radioGroup?: string | undefined; role?: React.AriaRole | undefined; about?: string | undefined; datatype?: string | undefined; inlist?: any; prefix?: string | undefined; property?: string | undefined; resource?: string | undefined; typeof?: string | undefined; vocab?: string | undefined; autoCapitalize?: string | undefined; autoCorrect?: string | undefined; autoSave?: string | undefined; itemProp?: string | undefined; itemScope?: boolean | undefined; itemType?: string | undefined; itemID?: string | undefined; itemRef?: string | undefined; results?: number | undefined; unselectable?: \"on\" | \"off\" | undefined; inputMode?: \"none\" | \"email\" | \"search\" | \"text\" | \"tel\" | \"url\" | \"numeric\" | \"decimal\" | undefined; is?: string | undefined; 'aria-activedescendant'?: string | undefined; 'aria-atomic'?: boolean | \"false\" | \"true\" | undefined; 'aria-autocomplete'?: \"none\" | \"list\" | \"inline\" | \"both\" | undefined; 'aria-busy'?: boolean | \"false\" | \"true\" | undefined; 'aria-checked'?: boolean | \"mixed\" | \"false\" | \"true\" | undefined; 'aria-colcount'?: number | undefined; 'aria-colindex'?: number | undefined; 'aria-colspan'?: number | undefined; 'aria-controls'?: string | undefined; 'aria-current'?: boolean | \"date\" | \"location\" | \"time\" | \"page\" | \"false\" | \"true\" | \"step\" | undefined; 'aria-describedby'?: string | undefined; 'aria-details'?: string | undefined; 'aria-disabled'?: boolean | \"false\" | \"true\" | undefined; 'aria-dropeffect'?: \"none\" | \"copy\" | \"link\" | \"execute\" | \"move\" | \"popup\" | undefined; 'aria-errormessage'?: string | undefined; 'aria-expanded'?: boolean | \"false\" | \"true\" | undefined; 'aria-flowto'?: string | undefined; 'aria-grabbed'?: boolean | \"false\" | \"true\" | undefined; 'aria-haspopup'?: boolean | \"grid\" | \"menu\" | \"false\" | \"true\" | \"dialog\" | \"listbox\" | \"tree\" | undefined; 'aria-hidden'?: boolean | \"false\" | \"true\" | undefined; 'aria-invalid'?: boolean | \"false\" | \"true\" | \"grammar\" | \"spelling\" | undefined; 'aria-keyshortcuts'?: string | undefined; 'aria-label'?: string | undefined; 'aria-labelledby'?: string | undefined; 'aria-level'?: number | undefined; 'aria-live'?: \"off\" | \"assertive\" | \"polite\" | undefined; 'aria-modal'?: boolean | \"false\" | \"true\" | undefined; 'aria-multiline'?: boolean | \"false\" | \"true\" | undefined; 'aria-multiselectable'?: boolean | \"false\" | \"true\" | undefined; 'aria-orientation'?: \"horizontal\" | \"vertical\" | undefined; 'aria-owns'?: string | undefined; 'aria-placeholder'?: string | undefined; 'aria-posinset'?: number | undefined; 'aria-pressed'?: boolean | \"mixed\" | \"false\" | \"true\" | undefined; 'aria-readonly'?: boolean | \"false\" | \"true\" | undefined; 'aria-relevant'?: \"all\" | \"text\" | \"additions\" | \"additions removals\" | \"additions text\" | \"removals\" | \"removals additions\" | \"removals text\" | \"text additions\" | \"text removals\" | undefined; 'aria-required'?: boolean | \"false\" | \"true\" | undefined; 'aria-roledescription'?: string | undefined; 'aria-rowcount'?: number | undefined; 'aria-rowindex'?: number | undefined; 'aria-rowspan'?: number | undefined; 'aria-selected'?: boolean | \"false\" | \"true\" | undefined; 'aria-setsize'?: number | undefined; 'aria-sort'?: \"none\" | \"other\" | \"ascending\" | \"descending\" | undefined; 'aria-valuemax'?: number | undefined; 'aria-valuemin'?: number | undefined; 'aria-valuenow'?: number | undefined; 'aria-valuetext'?: string | undefined; dangerouslySetInnerHTML?: { __html: string; } | undefined; onCopy?: React.ClipboardEventHandler | undefined; onCopyCapture?: React.ClipboardEventHandler | undefined; onCut?: React.ClipboardEventHandler | undefined; onCutCapture?: React.ClipboardEventHandler | undefined; onPaste?: React.ClipboardEventHandler | undefined; onPasteCapture?: React.ClipboardEventHandler | undefined; onCompositionEnd?: React.CompositionEventHandler | undefined; onCompositionEndCapture?: React.CompositionEventHandler | undefined; onCompositionStart?: React.CompositionEventHandler | undefined; onCompositionStartCapture?: React.CompositionEventHandler | undefined; onCompositionUpdate?: React.CompositionEventHandler | undefined; onCompositionUpdateCapture?: React.CompositionEventHandler | undefined; onFocus?: React.FocusEventHandler | undefined; onFocusCapture?: React.FocusEventHandler | undefined; onBlur?: React.FocusEventHandler | undefined; onBlurCapture?: React.FocusEventHandler | undefined; onChangeCapture?: React.FormEventHandler | undefined; onBeforeInput?: React.FormEventHandler | undefined; onBeforeInputCapture?: React.FormEventHandler | undefined; onInput?: React.FormEventHandler | undefined; onInputCapture?: React.FormEventHandler | undefined; onReset?: React.FormEventHandler | undefined; onResetCapture?: React.FormEventHandler | undefined; onSubmit?: React.FormEventHandler | undefined; onSubmitCapture?: React.FormEventHandler | undefined; onInvalid?: React.FormEventHandler | undefined; onInvalidCapture?: React.FormEventHandler | undefined; onLoad?: React.ReactEventHandler | undefined; onLoadCapture?: React.ReactEventHandler | undefined; onErrorCapture?: React.ReactEventHandler | undefined; onKeyDownCapture?: React.KeyboardEventHandler | undefined; onKeyPress?: React.KeyboardEventHandler | undefined; onKeyPressCapture?: React.KeyboardEventHandler | undefined; onKeyUp?: React.KeyboardEventHandler | undefined; onKeyUpCapture?: React.KeyboardEventHandler | undefined; onAbort?: React.ReactEventHandler | undefined; onAbortCapture?: React.ReactEventHandler | undefined; onCanPlay?: React.ReactEventHandler | undefined; onCanPlayCapture?: React.ReactEventHandler | undefined; onCanPlayThrough?: React.ReactEventHandler | undefined; onCanPlayThroughCapture?: React.ReactEventHandler | undefined; onDurationChange?: React.ReactEventHandler | undefined; onDurationChangeCapture?: React.ReactEventHandler | undefined; onEmptied?: React.ReactEventHandler | undefined; onEmptiedCapture?: React.ReactEventHandler | undefined; onEncrypted?: React.ReactEventHandler | undefined; onEncryptedCapture?: React.ReactEventHandler | undefined; onEnded?: React.ReactEventHandler | undefined; onEndedCapture?: React.ReactEventHandler | undefined; onLoadedData?: React.ReactEventHandler | undefined; onLoadedDataCapture?: React.ReactEventHandler | undefined; onLoadedMetadata?: React.ReactEventHandler | undefined; onLoadedMetadataCapture?: React.ReactEventHandler | undefined; onLoadStart?: React.ReactEventHandler | undefined; onLoadStartCapture?: React.ReactEventHandler | undefined; onPause?: React.ReactEventHandler | undefined; onPauseCapture?: React.ReactEventHandler | undefined; onPlay?: React.ReactEventHandler | undefined; onPlayCapture?: React.ReactEventHandler | undefined; onPlaying?: React.ReactEventHandler | undefined; onPlayingCapture?: React.ReactEventHandler | undefined; onProgress?: React.ReactEventHandler | undefined; onProgressCapture?: React.ReactEventHandler | undefined; onRateChange?: React.ReactEventHandler | undefined; onRateChangeCapture?: React.ReactEventHandler | undefined; onSeeked?: React.ReactEventHandler | undefined; onSeekedCapture?: React.ReactEventHandler | undefined; onSeeking?: React.ReactEventHandler | undefined; onSeekingCapture?: React.ReactEventHandler | undefined; onStalled?: React.ReactEventHandler | undefined; onStalledCapture?: React.ReactEventHandler | undefined; onSuspend?: React.ReactEventHandler | undefined; onSuspendCapture?: React.ReactEventHandler | undefined; onTimeUpdate?: React.ReactEventHandler | undefined; onTimeUpdateCapture?: React.ReactEventHandler | undefined; onVolumeChange?: React.ReactEventHandler | undefined; onVolumeChangeCapture?: React.ReactEventHandler | undefined; onWaiting?: React.ReactEventHandler | undefined; onWaitingCapture?: React.ReactEventHandler | undefined; onAuxClick?: React.MouseEventHandler | undefined; onAuxClickCapture?: React.MouseEventHandler | undefined; onClickCapture?: React.MouseEventHandler | undefined; onContextMenu?: React.MouseEventHandler | undefined; onContextMenuCapture?: React.MouseEventHandler | undefined; onDoubleClick?: React.MouseEventHandler | undefined; onDoubleClickCapture?: React.MouseEventHandler | undefined; onDrag?: React.DragEventHandler | undefined; onDragCapture?: React.DragEventHandler | undefined; onDragEnd?: React.DragEventHandler | undefined; onDragEndCapture?: React.DragEventHandler | undefined; onDragEnter?: React.DragEventHandler | undefined; onDragEnterCapture?: React.DragEventHandler | undefined; onDragExit?: React.DragEventHandler | undefined; onDragExitCapture?: React.DragEventHandler | undefined; onDragLeave?: React.DragEventHandler | undefined; onDragLeaveCapture?: React.DragEventHandler | undefined; onDragOver?: React.DragEventHandler | undefined; onDragOverCapture?: React.DragEventHandler | undefined; onDragStart?: React.DragEventHandler | undefined; onDragStartCapture?: React.DragEventHandler | undefined; onDrop?: React.DragEventHandler | undefined; onDropCapture?: React.DragEventHandler | undefined; onMouseDown?: React.MouseEventHandler | undefined; onMouseDownCapture?: React.MouseEventHandler | undefined; onMouseEnter?: React.MouseEventHandler | undefined; onMouseLeave?: React.MouseEventHandler | undefined; onMouseMove?: React.MouseEventHandler | undefined; onMouseMoveCapture?: React.MouseEventHandler | undefined; onMouseOut?: React.MouseEventHandler | undefined; onMouseOutCapture?: React.MouseEventHandler | undefined; onMouseOver?: React.MouseEventHandler | undefined; onMouseOverCapture?: React.MouseEventHandler | undefined; onMouseUp?: React.MouseEventHandler | undefined; onMouseUpCapture?: React.MouseEventHandler | undefined; onSelect?: React.ReactEventHandler | undefined; onSelectCapture?: React.ReactEventHandler | undefined; onTouchCancel?: React.TouchEventHandler | undefined; onTouchCancelCapture?: React.TouchEventHandler | undefined; onTouchEnd?: React.TouchEventHandler | undefined; onTouchEndCapture?: React.TouchEventHandler | undefined; onTouchMove?: React.TouchEventHandler | undefined; onTouchMoveCapture?: React.TouchEventHandler | undefined; onTouchStart?: React.TouchEventHandler | undefined; onTouchStartCapture?: React.TouchEventHandler | undefined; onPointerDown?: React.PointerEventHandler | undefined; onPointerDownCapture?: React.PointerEventHandler | undefined; onPointerMove?: React.PointerEventHandler | undefined; onPointerMoveCapture?: React.PointerEventHandler | undefined; onPointerUp?: React.PointerEventHandler | undefined; onPointerUpCapture?: React.PointerEventHandler | undefined; onPointerCancel?: React.PointerEventHandler | undefined; onPointerCancelCapture?: React.PointerEventHandler | undefined; onPointerEnter?: React.PointerEventHandler | undefined; onPointerEnterCapture?: React.PointerEventHandler | undefined; onPointerLeave?: React.PointerEventHandler | undefined; onPointerLeaveCapture?: React.PointerEventHandler | undefined; onPointerOver?: React.PointerEventHandler | undefined; onPointerOverCapture?: React.PointerEventHandler | undefined; onPointerOut?: React.PointerEventHandler | undefined; onPointerOutCapture?: React.PointerEventHandler | undefined; onGotPointerCapture?: React.PointerEventHandler | undefined; onGotPointerCaptureCapture?: React.PointerEventHandler | undefined; onLostPointerCapture?: React.PointerEventHandler | undefined; onLostPointerCaptureCapture?: React.PointerEventHandler | undefined; onScroll?: React.UIEventHandler | undefined; onScrollCapture?: React.UIEventHandler | undefined; onWheel?: React.WheelEventHandler | undefined; onWheelCapture?: React.WheelEventHandler | undefined; onAnimationStart?: React.AnimationEventHandler | undefined; onAnimationStartCapture?: React.AnimationEventHandler | undefined; onAnimationEnd?: React.AnimationEventHandler | undefined; onAnimationEndCapture?: React.AnimationEventHandler | undefined; onAnimationIteration?: React.AnimationEventHandler | undefined; onAnimationIterationCapture?: React.AnimationEventHandler | undefined; onTransitionEnd?: React.TransitionEventHandler | undefined; onTransitionEndCapture?: React.TransitionEventHandler | undefined; 'data-test-subj'?: string | undefined; height?: string | number | undefined; width?: string | undefined; readOnly?: boolean | undefined; align?: ", + "; defaultValue?: string | number | readonly string[] | undefined; lang?: string | undefined; defaultChecked?: boolean | undefined; suppressContentEditableWarning?: boolean | undefined; suppressHydrationWarning?: boolean | undefined; accessKey?: string | undefined; contentEditable?: \"inherit\" | Booleanish | undefined; contextMenu?: string | undefined; dir?: string | undefined; draggable?: Booleanish | undefined; placeholder?: string | undefined; slot?: string | undefined; spellCheck?: Booleanish | undefined; style?: React.CSSProperties | undefined; tabIndex?: number | undefined; translate?: \"no\" | \"yes\" | undefined; radioGroup?: string | undefined; role?: React.AriaRole | undefined; about?: string | undefined; datatype?: string | undefined; inlist?: any; prefix?: string | undefined; property?: string | undefined; resource?: string | undefined; typeof?: string | undefined; vocab?: string | undefined; autoCapitalize?: string | undefined; autoCorrect?: string | undefined; autoSave?: string | undefined; itemProp?: string | undefined; itemScope?: boolean | undefined; itemType?: string | undefined; itemID?: string | undefined; itemRef?: string | undefined; results?: number | undefined; unselectable?: \"on\" | \"off\" | undefined; inputMode?: \"none\" | \"email\" | \"search\" | \"text\" | \"tel\" | \"url\" | \"numeric\" | \"decimal\" | undefined; is?: string | undefined; 'aria-activedescendant'?: string | undefined; 'aria-atomic'?: Booleanish | undefined; 'aria-autocomplete'?: \"none\" | \"list\" | \"inline\" | \"both\" | undefined; 'aria-busy'?: Booleanish | undefined; 'aria-checked'?: boolean | \"mixed\" | \"false\" | \"true\" | undefined; 'aria-colcount'?: number | undefined; 'aria-colindex'?: number | undefined; 'aria-colspan'?: number | undefined; 'aria-controls'?: string | undefined; 'aria-current'?: boolean | \"date\" | \"location\" | \"time\" | \"page\" | \"false\" | \"true\" | \"step\" | undefined; 'aria-describedby'?: string | undefined; 'aria-details'?: string | undefined; 'aria-disabled'?: Booleanish | undefined; 'aria-dropeffect'?: \"none\" | \"copy\" | \"link\" | \"execute\" | \"move\" | \"popup\" | undefined; 'aria-errormessage'?: string | undefined; 'aria-expanded'?: Booleanish | undefined; 'aria-flowto'?: string | undefined; 'aria-grabbed'?: Booleanish | undefined; 'aria-haspopup'?: boolean | \"grid\" | \"menu\" | \"false\" | \"true\" | \"dialog\" | \"listbox\" | \"tree\" | undefined; 'aria-hidden'?: Booleanish | undefined; 'aria-invalid'?: boolean | \"false\" | \"true\" | \"grammar\" | \"spelling\" | undefined; 'aria-keyshortcuts'?: string | undefined; 'aria-label'?: string | undefined; 'aria-labelledby'?: string | undefined; 'aria-level'?: number | undefined; 'aria-live'?: \"off\" | \"assertive\" | \"polite\" | undefined; 'aria-modal'?: Booleanish | undefined; 'aria-multiline'?: Booleanish | undefined; 'aria-multiselectable'?: Booleanish | undefined; 'aria-orientation'?: \"horizontal\" | \"vertical\" | undefined; 'aria-owns'?: string | undefined; 'aria-placeholder'?: string | undefined; 'aria-posinset'?: number | undefined; 'aria-pressed'?: boolean | \"mixed\" | \"false\" | \"true\" | undefined; 'aria-readonly'?: Booleanish | undefined; 'aria-relevant'?: \"all\" | \"text\" | \"additions\" | \"additions removals\" | \"additions text\" | \"removals\" | \"removals additions\" | \"removals text\" | \"text additions\" | \"text removals\" | undefined; 'aria-required'?: Booleanish | undefined; 'aria-roledescription'?: string | undefined; 'aria-rowcount'?: number | undefined; 'aria-rowindex'?: number | undefined; 'aria-rowspan'?: number | undefined; 'aria-selected'?: Booleanish | undefined; 'aria-setsize'?: number | undefined; 'aria-sort'?: \"none\" | \"other\" | \"ascending\" | \"descending\" | undefined; 'aria-valuemax'?: number | undefined; 'aria-valuemin'?: number | undefined; 'aria-valuenow'?: number | undefined; 'aria-valuetext'?: string | undefined; dangerouslySetInnerHTML?: { __html: string; } | undefined; onCopy?: React.ClipboardEventHandler | undefined; onCopyCapture?: React.ClipboardEventHandler | undefined; onCut?: React.ClipboardEventHandler | undefined; onCutCapture?: React.ClipboardEventHandler | undefined; onPaste?: React.ClipboardEventHandler | undefined; onPasteCapture?: React.ClipboardEventHandler | undefined; onCompositionEnd?: React.CompositionEventHandler | undefined; onCompositionEndCapture?: React.CompositionEventHandler | undefined; onCompositionStart?: React.CompositionEventHandler | undefined; onCompositionStartCapture?: React.CompositionEventHandler | undefined; onCompositionUpdate?: React.CompositionEventHandler | undefined; onCompositionUpdateCapture?: React.CompositionEventHandler | undefined; onFocus?: React.FocusEventHandler | undefined; onFocusCapture?: React.FocusEventHandler | undefined; onBlur?: React.FocusEventHandler | undefined; onBlurCapture?: React.FocusEventHandler | undefined; onChangeCapture?: React.FormEventHandler | undefined; onBeforeInput?: React.FormEventHandler | undefined; onBeforeInputCapture?: React.FormEventHandler | undefined; onInput?: React.FormEventHandler | undefined; onInputCapture?: React.FormEventHandler | undefined; onReset?: React.FormEventHandler | undefined; onResetCapture?: React.FormEventHandler | undefined; onSubmit?: React.FormEventHandler | undefined; onSubmitCapture?: React.FormEventHandler | undefined; onInvalid?: React.FormEventHandler | undefined; onInvalidCapture?: React.FormEventHandler | undefined; onLoad?: React.ReactEventHandler | undefined; onLoadCapture?: React.ReactEventHandler | undefined; onErrorCapture?: React.ReactEventHandler | undefined; onKeyDownCapture?: React.KeyboardEventHandler | undefined; onKeyPress?: React.KeyboardEventHandler | undefined; onKeyPressCapture?: React.KeyboardEventHandler | undefined; onKeyUp?: React.KeyboardEventHandler | undefined; onKeyUpCapture?: React.KeyboardEventHandler | undefined; onAbort?: React.ReactEventHandler | undefined; onAbortCapture?: React.ReactEventHandler | undefined; onCanPlay?: React.ReactEventHandler | undefined; onCanPlayCapture?: React.ReactEventHandler | undefined; onCanPlayThrough?: React.ReactEventHandler | undefined; onCanPlayThroughCapture?: React.ReactEventHandler | undefined; onDurationChange?: React.ReactEventHandler | undefined; onDurationChangeCapture?: React.ReactEventHandler | undefined; onEmptied?: React.ReactEventHandler | undefined; onEmptiedCapture?: React.ReactEventHandler | undefined; onEncrypted?: React.ReactEventHandler | undefined; onEncryptedCapture?: React.ReactEventHandler | undefined; onEnded?: React.ReactEventHandler | undefined; onEndedCapture?: React.ReactEventHandler | undefined; onLoadedData?: React.ReactEventHandler | undefined; onLoadedDataCapture?: React.ReactEventHandler | undefined; onLoadedMetadata?: React.ReactEventHandler | undefined; onLoadedMetadataCapture?: React.ReactEventHandler | undefined; onLoadStart?: React.ReactEventHandler | undefined; onLoadStartCapture?: React.ReactEventHandler | undefined; onPause?: React.ReactEventHandler | undefined; onPauseCapture?: React.ReactEventHandler | undefined; onPlay?: React.ReactEventHandler | undefined; onPlayCapture?: React.ReactEventHandler | undefined; onPlaying?: React.ReactEventHandler | undefined; onPlayingCapture?: React.ReactEventHandler | undefined; onProgress?: React.ReactEventHandler | undefined; onProgressCapture?: React.ReactEventHandler | undefined; onRateChange?: React.ReactEventHandler | undefined; onRateChangeCapture?: React.ReactEventHandler | undefined; onSeeked?: React.ReactEventHandler | undefined; onSeekedCapture?: React.ReactEventHandler | undefined; onSeeking?: React.ReactEventHandler | undefined; onSeekingCapture?: React.ReactEventHandler | undefined; onStalled?: React.ReactEventHandler | undefined; onStalledCapture?: React.ReactEventHandler | undefined; onSuspend?: React.ReactEventHandler | undefined; onSuspendCapture?: React.ReactEventHandler | undefined; onTimeUpdate?: React.ReactEventHandler | undefined; onTimeUpdateCapture?: React.ReactEventHandler | undefined; onVolumeChange?: React.ReactEventHandler | undefined; onVolumeChangeCapture?: React.ReactEventHandler | undefined; onWaiting?: React.ReactEventHandler | undefined; onWaitingCapture?: React.ReactEventHandler | undefined; onAuxClick?: React.MouseEventHandler | undefined; onAuxClickCapture?: React.MouseEventHandler | undefined; onClickCapture?: React.MouseEventHandler | undefined; onContextMenu?: React.MouseEventHandler | undefined; onContextMenuCapture?: React.MouseEventHandler | undefined; onDoubleClick?: React.MouseEventHandler | undefined; onDoubleClickCapture?: React.MouseEventHandler | undefined; onDrag?: React.DragEventHandler | undefined; onDragCapture?: React.DragEventHandler | undefined; onDragEnd?: React.DragEventHandler | undefined; onDragEndCapture?: React.DragEventHandler | undefined; onDragEnter?: React.DragEventHandler | undefined; onDragEnterCapture?: React.DragEventHandler | undefined; onDragExit?: React.DragEventHandler | undefined; onDragExitCapture?: React.DragEventHandler | undefined; onDragLeave?: React.DragEventHandler | undefined; onDragLeaveCapture?: React.DragEventHandler | undefined; onDragOver?: React.DragEventHandler | undefined; onDragOverCapture?: React.DragEventHandler | undefined; onDragStart?: React.DragEventHandler | undefined; onDragStartCapture?: React.DragEventHandler | undefined; onDrop?: React.DragEventHandler | undefined; onDropCapture?: React.DragEventHandler | undefined; onMouseDown?: React.MouseEventHandler | undefined; onMouseDownCapture?: React.MouseEventHandler | undefined; onMouseEnter?: React.MouseEventHandler | undefined; onMouseLeave?: React.MouseEventHandler | undefined; onMouseMove?: React.MouseEventHandler | undefined; onMouseMoveCapture?: React.MouseEventHandler | undefined; onMouseOut?: React.MouseEventHandler | undefined; onMouseOutCapture?: React.MouseEventHandler | undefined; onMouseOver?: React.MouseEventHandler | undefined; onMouseOverCapture?: React.MouseEventHandler | undefined; onMouseUp?: React.MouseEventHandler | undefined; onMouseUpCapture?: React.MouseEventHandler | undefined; onSelect?: React.ReactEventHandler | undefined; onSelectCapture?: React.ReactEventHandler | undefined; onTouchCancel?: React.TouchEventHandler | undefined; onTouchCancelCapture?: React.TouchEventHandler | undefined; onTouchEnd?: React.TouchEventHandler | undefined; onTouchEndCapture?: React.TouchEventHandler | undefined; onTouchMove?: React.TouchEventHandler | undefined; onTouchMoveCapture?: React.TouchEventHandler | undefined; onTouchStart?: React.TouchEventHandler | undefined; onTouchStartCapture?: React.TouchEventHandler | undefined; onPointerDown?: React.PointerEventHandler | undefined; onPointerDownCapture?: React.PointerEventHandler | undefined; onPointerMove?: React.PointerEventHandler | undefined; onPointerMoveCapture?: React.PointerEventHandler | undefined; onPointerUp?: React.PointerEventHandler | undefined; onPointerUpCapture?: React.PointerEventHandler | undefined; onPointerCancel?: React.PointerEventHandler | undefined; onPointerCancelCapture?: React.PointerEventHandler | undefined; onPointerEnter?: React.PointerEventHandler | undefined; onPointerEnterCapture?: React.PointerEventHandler | undefined; onPointerLeave?: React.PointerEventHandler | undefined; onPointerLeaveCapture?: React.PointerEventHandler | undefined; onPointerOver?: React.PointerEventHandler | undefined; onPointerOverCapture?: React.PointerEventHandler | undefined; onPointerOut?: React.PointerEventHandler | undefined; onPointerOutCapture?: React.PointerEventHandler | undefined; onGotPointerCapture?: React.PointerEventHandler | undefined; onGotPointerCaptureCapture?: React.PointerEventHandler | undefined; onLostPointerCapture?: React.PointerEventHandler | undefined; onLostPointerCaptureCapture?: React.PointerEventHandler | undefined; onScroll?: React.UIEventHandler | undefined; onScrollCapture?: React.UIEventHandler | undefined; onWheel?: React.WheelEventHandler | undefined; onWheelCapture?: React.WheelEventHandler | undefined; onAnimationStart?: React.AnimationEventHandler | undefined; onAnimationStartCapture?: React.AnimationEventHandler | undefined; onAnimationEnd?: React.AnimationEventHandler | undefined; onAnimationEndCapture?: React.AnimationEventHandler | undefined; onAnimationIteration?: React.AnimationEventHandler | undefined; onAnimationIterationCapture?: React.AnimationEventHandler | undefined; onTransitionEnd?: React.TransitionEventHandler | undefined; onTransitionEndCapture?: React.TransitionEventHandler | undefined; 'data-test-subj'?: string | undefined; height?: string | number | undefined; width?: string | undefined; readOnly?: boolean | undefined; align?: ", "HorizontalAlignment", " | undefined; abbr?: string | undefined; footer?: string | React.ReactElement> | ((props: ", "EuiTableFooterProps", diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx index dde72e16e9079..eba0b351cf94b 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: 2022-08-19 +date: 2022-08-23 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 2bf53ddb566a3..bd705560dbc0b 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: 2022-08-19 +date: 2022-08-23 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 b9f86b6b0b275..e6e194f01a620 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: 2022-08-19 +date: 2022-08-23 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 3d2d9dad95742..a31365834c59a 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: 2022-08-19 +date: 2022-08-23 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 57f50b37534be..a2241ec00fa20 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: 2022-08-19 +date: 2022-08-23 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 4074e5f2a920d..5413d3758f25c 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: 2022-08-19 +date: 2022-08-23 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 7619869165dfa..cadb6596d17d4 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] --- import securityObj from './security.devdocs.json'; diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index a6caa4ceb3651..92bf59787afc9 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index 4b5f48e3237f1..f2e6f2ee5f2b5 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: 2022-08-19 +date: 2022-08-23 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 8eb11158e0d6d..d90c34dadbb6d 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: 2022-08-19 +date: 2022-08-23 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 39437784fa987..6154f66889c80 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: 2022-08-19 +date: 2022-08-23 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 5bbd53b86d25b..e997e576cb621 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: 2022-08-19 +date: 2022-08-23 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 40fe30e3279b7..11e7a1347ad53 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackAlerts'] --- import stackAlertsObj from './stack_alerts.devdocs.json'; diff --git a/api_docs/task_manager.mdx b/api_docs/task_manager.mdx index 02ae113a01566..ea654495c377d 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: 2022-08-19 +date: 2022-08-23 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 b7dee9470fa25..8d087a165237e 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: 2022-08-19 +date: 2022-08-23 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 49112fbcaee34..5e62d1ce2cc7f 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: 2022-08-19 +date: 2022-08-23 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 90b788522bba6..6ee8f33cf9ce5 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: 2022-08-19 +date: 2022-08-23 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 229e1a346c8c9..43447eed379f6 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/threat_intelligence.devdocs.json b/api_docs/threat_intelligence.devdocs.json index 380afebb238bf..cc29bb3584111 100644 --- a/api_docs/threat_intelligence.devdocs.json +++ b/api_docs/threat_intelligence.devdocs.json @@ -132,6 +132,66 @@ } ], "interfaces": [ + { + "parentPluginId": "threatIntelligence", + "id": "def-public.SecuritySolutionPluginContext", + "type": "Interface", + "tags": [], + "label": "SecuritySolutionPluginContext", + "description": [ + "\nMethods exposed from the security solution to the threat intelligence application." + ], + "path": "x-pack/plugins/threat_intelligence/public/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "threatIntelligence", + "id": "def-public.SecuritySolutionPluginContext.getFiltersGlobalComponent", + "type": "Function", + "tags": [], + "label": "getFiltersGlobalComponent", + "description": [ + "\nGets the `FiltersGlobal` component for embedding a filter bar in the security solution application." + ], + "signature": [ + "() => React.ComponentType<{ children: React.ReactNode; }>" + ], + "path": "x-pack/plugins/threat_intelligence/public/types.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "threatIntelligence", + "id": "def-public.SecuritySolutionPluginContext.licenseService", + "type": "Object", + "tags": [], + "label": "licenseService", + "description": [ + "\nGet the user's license to drive the Threat Intelligence plugin's visibility." + ], + "signature": [ + "LicenseAware" + ], + "path": "x-pack/plugins/threat_intelligence/public/types.ts", + "deprecated": false + }, + { + "parentPluginId": "threatIntelligence", + "id": "def-public.SecuritySolutionPluginContext.sourcererDataView", + "type": "Object", + "tags": [], + "label": "sourcererDataView", + "description": [], + "signature": [ + "SourcererDataView" + ], + "path": "x-pack/plugins/threat_intelligence/public/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "threatIntelligence", "id": "def-public.ThreatIntelligencePluginSetup", @@ -167,8 +227,8 @@ "pluginId": "threatIntelligence", "scope": "public", "docId": "kibThreatIntelligencePluginApi", - "section": "def-public.ThreatIntelligenceSecuritySolutionContext", - "text": "ThreatIntelligenceSecuritySolutionContext" + "section": "def-public.SecuritySolutionPluginContext", + "text": "SecuritySolutionPluginContext" }, "; }) => React.ReactElement>" ], @@ -179,53 +239,6 @@ } ], "initialIsOpen": false - }, - { - "parentPluginId": "threatIntelligence", - "id": "def-public.ThreatIntelligenceSecuritySolutionContext", - "type": "Interface", - "tags": [], - "label": "ThreatIntelligenceSecuritySolutionContext", - "description": [ - "\nMethods exposed from the security solution to the threat intelligence application." - ], - "path": "x-pack/plugins/threat_intelligence/public/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "threatIntelligence", - "id": "def-public.ThreatIntelligenceSecuritySolutionContext.getFiltersGlobalComponent", - "type": "Function", - "tags": [], - "label": "getFiltersGlobalComponent", - "description": [ - "\nGets the `FiltersGlobal` component for embedding a filter bar in the security solution application." - ], - "signature": [ - "() => React.ComponentType<{ children: React.ReactNode; }>" - ], - "path": "x-pack/plugins/threat_intelligence/public/types.ts", - "deprecated": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "threatIntelligence", - "id": "def-public.ThreatIntelligenceSecuritySolutionContext.licenseService", - "type": "Object", - "tags": [], - "label": "licenseService", - "description": [ - "\nGet the user's license to drive the Threat Intelligence plugin's visibility." - ], - "signature": [ - "LicenseAware" - ], - "path": "x-pack/plugins/threat_intelligence/public/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false } ], "enums": [], diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index 488837075f579..acc6772f224d6 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'threatIntelligence'] --- import threatIntelligenceObj from './threat_intelligence.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Protections Experience Team](https://github.com/orgs/elastic/teams/prot | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 15 | 0 | 3 | 1 | +| 16 | 0 | 4 | 2 | ## Client diff --git a/api_docs/timelines.mdx b/api_docs/timelines.mdx index 8592a9cd703fa..da1215c0693e2 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: 2022-08-19 +date: 2022-08-23 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 a9aacacc48211..e5fe712b5b489 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: 2022-08-19 +date: 2022-08-23 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 5fcb3db2baceb..8a9b20c2a05ce 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: 2022-08-19 +date: 2022-08-23 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 c5a478799b396..9ea4163fe785f 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: 2022-08-19 +date: 2022-08-23 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 5e0c075fcdcdd..7a3f7450c8baa 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 887a71399be84..6e3cb31272fc9 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: 2022-08-19 +date: 2022-08-23 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 2ae9c0ae00af9..59e01e601153b 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: 2022-08-19 +date: 2022-08-23 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 0924c5aa66b32..fcaf32592b753 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: 2022-08-19 +date: 2022-08-23 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 2968301445bc3..3c02fa62b3a67 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: 2022-08-19 +date: 2022-08-23 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 817a60218f8d6..11caefeca80a4 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: 2022-08-19 +date: 2022-08-23 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 a4f685265ba95..6ba49f66004f9 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: 2022-08-19 +date: 2022-08-23 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 2c5824214bc27..843fd83cfe35e 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: 2022-08-19 +date: 2022-08-23 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 c8e59d379157c..9bcfb92ec93cb 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: 2022-08-19 +date: 2022-08-23 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 074ec5d6e70f1..e48151d177425 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: 2022-08-19 +date: 2022-08-23 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 4cc89f918336d..849e3c38b7f3e 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: 2022-08-19 +date: 2022-08-23 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 5758c3dc95588..9bafd8cb97de8 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: 2022-08-19 +date: 2022-08-23 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 fe5819a472bb9..b628dc6f57e31 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: 2022-08-19 +date: 2022-08-23 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 24fa91a1c524c..7ab92e676f3ba 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: 2022-08-19 +date: 2022-08-23 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 bda5b6fb215d1..f2b331492f7d2 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: 2022-08-19 +date: 2022-08-23 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 426d948227e53..69079cebb44cf 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.devdocs.json b/api_docs/visualizations.devdocs.json index c706825202fd6..15691cacaad28 100644 --- a/api_docs/visualizations.devdocs.json +++ b/api_docs/visualizations.devdocs.json @@ -99,6 +99,14 @@ "section": "def-common.VisParams", "text": "VisParams" }, + " | undefined, timeRange?: ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.TimeRange", + "text": "TimeRange" + }, " | undefined) => Promise<", { "pluginId": "visualizations", @@ -2870,7 +2878,7 @@ "label": "configuration", "description": [], "signature": [ - "{ fill: string | number; legend: { isVisible: boolean; position: string; shouldTruncate: boolean; maxLines: number; showSingleSeries: boolean; }; gridLinesVisibility: { x: boolean; yLeft: boolean; yRight: boolean; }; extents: { yLeftExtent: AxisExtents; yRightExtent: AxisExtents; }; }" + "{ fill: string | number; legend: { isVisible: boolean; position: string; shouldTruncate: boolean; maxLines: number; showSingleSeries: boolean; }; gridLinesVisibility: { x: boolean; yLeft: boolean; yRight: boolean; }; tickLabelsVisibility?: { x: boolean; yLeft: boolean; yRight: boolean; } | undefined; axisTitlesVisibility?: { x: boolean; yLeft: boolean; yRight: boolean; } | undefined; valueLabels?: boolean | undefined; extents?: { yLeftExtent: AxisExtents; yRightExtent: AxisExtents; } | undefined; }" ], "path": "src/plugins/visualizations/public/vis_types/types.ts", "deprecated": false @@ -4308,6 +4316,14 @@ "section": "def-common.VisParams", "text": "VisParams" }, + " | undefined, timeRange?: ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.TimeRange", + "text": "TimeRange" + }, " | undefined) => Promise<", { "pluginId": "visualizations", @@ -4341,6 +4357,27 @@ "path": "src/plugins/visualizations/public/vis_types/types.ts", "deprecated": false, "isRequired": false + }, + { + "parentPluginId": "visualizations", + "id": "def-public.VisTypeDefinition.navigateToLens.$2", + "type": "Object", + "tags": [], + "label": "timeRange", + "description": [], + "signature": [ + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.TimeRange", + "text": "TimeRange" + }, + " | undefined" + ], + "path": "src/plugins/visualizations/public/vis_types/types.ts", + "deprecated": false, + "isRequired": false } ], "returnComment": [] @@ -5139,10 +5176,23 @@ }, { "parentPluginId": "visualizations", - "id": "def-public.VisualizeEditorLayersContext.timeFieldName", + "id": "def-public.VisualizeEditorLayersContext.xFieldName", + "type": "string", + "tags": [], + "label": "xFieldName", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/visualizations/public/vis_types/types.ts", + "deprecated": false + }, + { + "parentPluginId": "visualizations", + "id": "def-public.VisualizeEditorLayersContext.xMode", "type": "string", "tags": [], - "label": "timeFieldName", + "label": "xMode", "description": [], "signature": [ "string | undefined" diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index 1ae163836d5c3..c2caf3af66c98 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: 2022-08-19 +date: 2022-08-23 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; @@ -21,7 +21,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 414 | 12 | 386 | 15 | +| 416 | 12 | 388 | 15 | ## Client diff --git a/dev_docs/contributing/best_practices.mdx b/dev_docs/contributing/best_practices.mdx index 16c66417b89c1..7ec289561c816 100644 --- a/dev_docs/contributing/best_practices.mdx +++ b/dev_docs/contributing/best_practices.mdx @@ -70,7 +70,7 @@ const dataView = savedObjectsClient.get(dataViewId) as DataView; ## Resusable react components -Use [EUI](https://elastic.github.io/eui) for all your basic UI components to create a consistent UI experience. We also have generic UI components offered from the plugin and the plugin. +Use [EUI](https://elastic.github.io/eui) for all your basic UI components to create a consistent UI experience. We also have generic UI components offered from the plugin. ## Don't export code that doesn't need to be public diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 00f0bc517f5a5..e4bda1d283444 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -299,6 +299,10 @@ In general this plugin provides: |Registers commercially licensed generic actions like per panel time range and contains some code that supports drilldown work. +|{kib-repo}blob/{branch}/src/plugins/unified_field_list/README.md[unifiedFieldList] +|This Kibana plugin contains components and services for field list UI (as in fields sidebar on Discover and Lens pages). + + |{kib-repo}blob/{branch}/src/plugins/unified_search/README.md[unifiedSearch] |Contains all the components of Kibana's unified search experience. Specifically: diff --git a/docs/maps/images/reverse-geocoding-tutorial/csa_regions.jpeg b/docs/maps/images/reverse-geocoding-tutorial/csa_regions.jpeg deleted file mode 100644 index 07435954a0b0b..0000000000000 Binary files a/docs/maps/images/reverse-geocoding-tutorial/csa_regions.jpeg and /dev/null differ diff --git a/docs/maps/images/reverse-geocoding-tutorial/csa_regions.png b/docs/maps/images/reverse-geocoding-tutorial/csa_regions.png new file mode 100644 index 0000000000000..912814d50c292 Binary files /dev/null and b/docs/maps/images/reverse-geocoding-tutorial/csa_regions.png differ diff --git a/docs/maps/images/reverse-geocoding-tutorial/csa_regions_by_web_traffic.png b/docs/maps/images/reverse-geocoding-tutorial/csa_regions_by_web_traffic.png index 491448920ec49..8f107f761f342 100644 Binary files a/docs/maps/images/reverse-geocoding-tutorial/csa_regions_by_web_traffic.png and b/docs/maps/images/reverse-geocoding-tutorial/csa_regions_by_web_traffic.png differ diff --git a/docs/maps/images/reverse-geocoding-tutorial/discover_enriched_web_log.png b/docs/maps/images/reverse-geocoding-tutorial/discover_enriched_web_log.png index 9c29da2d17947..ef8fde0cd4f22 100644 Binary files a/docs/maps/images/reverse-geocoding-tutorial/discover_enriched_web_log.png and b/docs/maps/images/reverse-geocoding-tutorial/discover_enriched_web_log.png differ diff --git a/docs/maps/reverse-geocoding-tutorial.asciidoc b/docs/maps/reverse-geocoding-tutorial.asciidoc index 5997637b2eb13..059a7ec4e7b83 100644 --- a/docs/maps/reverse-geocoding-tutorial.asciidoc +++ b/docs/maps/reverse-geocoding-tutorial.asciidoc @@ -2,7 +2,7 @@ [[reverse-geocoding-tutorial]] == Map custom regions with reverse geocoding -*Maps* comes with https://maps.elastic.co/#file[predefined regions] that allow you to quickly visualize regions by metrics. *Maps* also offers the ability to map your own regions. You can use any region data you'd like, as long as your source data contains an identifier for the corresponding region. +*Maps* comes with https://maps.elastic.co/#file[predefined regions] that allow you to quickly visualize regions by metrics. *Maps* also offers the ability to map your own regions. You can use any region data you'd like, as long as your source data contains an identifier for the corresponding region. But how can you map regions when your source data does not contain a region identifier? This is where reverse geocoding comes in. Reverse geocoding is the process of assigning a region identifier to a feature based on its location. @@ -17,7 +17,7 @@ You’ll learn to: When you complete this tutorial, you’ll have a map that looks like this: [role="screenshot"] -image::maps/images/reverse-geocoding-tutorial/csa_regions_by_web_traffic.png[] +image::maps/images/reverse-geocoding-tutorial/csa_regions_by_web_traffic.png[Map showing custom regions] [float] @@ -29,6 +29,7 @@ You’ll use the <> that comes with Kiban To install web logs sample data set: . On the home page, click *Try sample data*. +. Expand *Other sample data sets*. . On the *Sample web logs* card, click *Add data*. @@ -36,13 +37,14 @@ To install web logs sample data set: === Step 2: Index Combined Statistical Area (CSA) regions GeoIP level of detail is very useful for driving decision-making. For example, say you want to spin up a marketing campaign based on the locations of your users or show executive stakeholders which metro areas are experiencing an uptick of traffic. -That kind of scale in the United States is often captured with what the Census Bureau calls the Combined Statistical Area (CSA). Combined Statistical Area is roughly equivalent with how people intuitively think of which urban area they live in. It does not necessarily coincide with state or city boundaries. +That kind of scale in the United States is often captured with what the Census +Bureau calls the Combined Statistical Area (CSA). CSA is roughly equivalent with how people intuitively think of which urban area they live in. It does not necessarily coincide with state or city boundaries. CSAs generally share the same telecom providers and ad networks. New fast food franchises expand to a CSA rather than a particular city or municipality. Basically, people in the same CSA shop in the same IKEA. To get the CSA boundary data: -. Download the "Combined Statistical Areas (CSAs)" zip file from https://www.census.gov/geographies/mapping-files/time-series/geo/carto-boundary-file.html[Census Bureau’s website]. +. Go to the https://www.census.gov/geographies/mapping-files/time-series/geo/carto-boundary-file.html[Census Bureau’s website] and download the `cb_2018_us_csa_500k.zip` file. . Uncompress the zip file. . In Kibana, open the main menu, and click *Maps*. . Click *Create map*. @@ -55,7 +57,7 @@ To get the CSA boundary data: . Set index name to *csa* and click *Import file*. . When importing is complete, click *Add as document layer*. . Add Tooltip fields: -.. Click *+ Add* to open field select. +.. Click *+ Add* to open the field select. .. Select *NAME*, *GEOID*, and *AFFGEOID*. .. Click *Add*. . Click *Save & close*. @@ -63,24 +65,24 @@ To get the CSA boundary data: Looking at the map, you get a sense of what constitutes a metro area in the eyes of the Census Bureau. [role="screenshot"] -image::maps/images/reverse-geocoding-tutorial/csa_regions.jpeg[] +image::maps/images/reverse-geocoding-tutorial/csa_regions.png[Map showing metro area] [float] === Step 3: Reverse geocoding To visualize CSA regions by web log traffic, the web log traffic must contain a CSA region identifier. You'll use {es} {ref}/enrich-processor.html[enrich processor] to add CSA region identifiers to the web logs sample data set. You can skip this step if your source data already contains region identifiers. -. Open the main menu, then click *Dev Tools*. +. Open the main menu, and then click *Dev Tools*. . In *Console*, create a {ref}/geo-match-enrich-policy-type.html[geo_match enrichment policy]: + [source,js] ---------------------------------- -PUT /_enrich/policy/csa_lookup -{ - "geo_match": { - "indices": "csa", - "match_field": "coordinates", - "enrich_fields": [ "GEOID", "NAME"] - } +PUT /_enrich/policy/csa_lookup +{ + "geo_match": { + "indices": "csa", + "match_field": "geometry", + "enrich_fields": [ "GEOID", "NAME"] + } } ---------------------------------- @@ -95,29 +97,29 @@ POST /_enrich/policy/csa_lookup/_execute + [source,js] ---------------------------------- -PUT _ingest/pipeline/lonlat-to-csa -{ - "description": "Reverse geocode longitude-latitude to combined statistical area", - "processors": [ - { - "enrich": { - "field": "geo.coordinates", - "policy_name": "csa_lookup", - "target_field": "csa", - "ignore_missing": true, - "ignore_failure": true, - "description": "Lookup the csa identifier" - } - }, - { - "remove": { - "field": "csa.coordinates", - "ignore_missing": true, - "ignore_failure": true, - "description": "Remove the shape field" - } - } - ] +PUT _ingest/pipeline/lonlat-to-csa +{ + "description": "Reverse geocode longitude-latitude to combined statistical area", + "processors": [ + { + "enrich": { + "field": "geo.coordinates", + "policy_name": "csa_lookup", + "target_field": "csa", + "ignore_missing": true, + "ignore_failure": true, + "description": "Lookup the csa identifier" + } + }, + { + "remove": { + "field": "csa.geometry", + "ignore_missing": true, + "ignore_failure": true, + "description": "Remove the shape field" + } + } + ] } ---------------------------------- @@ -132,16 +134,16 @@ POST kibana_sample_data_logs/_update_by_query?pipeline=lonlat-to-csa + [source,js] ---------------------------------- -PUT kibana_sample_data_logs/_settings -{ - "index": { - "default_pipeline": "lonlat-to-csa" - } +PUT kibana_sample_data_logs/_settings +{ + "index": { + "default_pipeline": "lonlat-to-csa" + } } ---------------------------------- . Open the main menu, and click *Discover*. -. Set the data view to *kibana_sample_data_logs*. +. Set the data view to *Kibana Sample Data Logs*. . Open the <>, and set the time range to the last 30 days. . Scan through the list of *Available fields* until you find the `csa.GEOID` field. You can also search for the field by name. . Click image:images/reverse-geocoding-tutorial/add-icon.png[Add icon] to toggle the field into the document table. @@ -150,7 +152,7 @@ PUT kibana_sample_data_logs/_settings Your web log data now contains `csa.GEOID` and `csa.NAME` fields from the matching *csa* region. Web log traffic not contained in a CSA region does not have values for `csa.GEOID` and `csa.NAME` fields. [role="screenshot"] -image::maps/images/reverse-geocoding-tutorial/discover_enriched_web_log.png[] +image::maps/images/reverse-geocoding-tutorial/discover_enriched_web_log.png[View of data in Discover] [float] === Step 4: Visualize Combined Statistical Area (CSA) regions by web traffic @@ -160,12 +162,12 @@ Now that our web traffic contains CSA region identifiers, you'll visualize CSA r . Click *Create map*. . Click *Add layer*. . Click *Choropleth*. -. For *Boundaries source*: +. For *Boundaries source*: .. Select *Points, lines, and polygons from Elasticsearch*. .. Set *Data view* to *csa*. .. Set *Join field* to *GEOID*. . For *Statistics source*: -.. Set *Data view* to *kibana_sample_data_logs*. +.. Set *Data view* to *Kibana Sample Data Logs*. .. Set *Join field* to *csa.GEOID.keyword*. . Click *Add layer*. . Scroll to *Layer Style* and Set *Label* to *Fixed*. @@ -176,7 +178,6 @@ Now that our web traffic contains CSA region identifiers, you'll visualize CSA r .. Click *Save and add to library*. [role="screenshot"] -image::maps/images/reverse-geocoding-tutorial/csa_regions_by_web_traffic.png[] +image::maps/images/reverse-geocoding-tutorial/csa_regions_by_web_traffic.png[Final map showing custom regions] Congratulations! You have completed the tutorial and have the recipe for visualizing custom regions. You can now try replicating this same analysis with your own data. - diff --git a/docs/osquery/images/enter-query.png b/docs/osquery/images/enter-query.png index efcf8fa58a7a8..6043eb3632982 100644 Binary files a/docs/osquery/images/enter-query.png and b/docs/osquery/images/enter-query.png differ diff --git a/docs/osquery/osquery.asciidoc b/docs/osquery/osquery.asciidoc index 88f8f768df0de..66edbc95526eb 100644 --- a/docs/osquery/osquery.asciidoc +++ b/docs/osquery/osquery.asciidoc @@ -37,26 +37,25 @@ To inspect hosts, run a query against one or more agents or policies, then view the results. . Open the main menu, and then click *Osquery*. - . In the *Live queries* view, click **New live query**. - +. Choose to run a single query or a query pack. . Select one or more agents or groups to query. Start typing in the search field, and you'll get suggestions for agents by name, ID, platform, and policy. - -. Enter a query or select a query from your saved queries. +. Specify the query or pack to run: +** *Query*: Select a saved query or enter a new one in the text box. After you enter the query, you can expand the **Advanced** section to view or set <> included in the results from the live query. Mapping ECS fields is optional. +** *Pack*: Select from query packs that have been loaded and activated. After you select a pack, all of the queries in the pack are displayed. ++ +TIP: Refer to <> to learn about using and managing Elastic prebuilt packs. + [role="screenshot"] image::images/enter-query.png[Select saved query dropdown name showing query name and description] -. (Optional) Expand the **Advanced** section to view or set <> included in the results from the live query. - -. Click **Submit**. +. Click **Submit**. Queries will timeout after 5 minutes if there are no responses. ++ +TIP: To save a single query for future use, click *Save for later* and define the ID, description, and other <>. -. Review the results in a table, or navigate to *Discover* to dive deeper into the response, -or to the drag-and-drop *Lens* editor to create visualizations. +. Review the results. Next, navigate to *Discover* to dive deeper into the response or to *Lens* to create visualizations. . To view more information about the request, such as failures, open the *Status* tab. -. To save the query for future use, click *Save for later* and define the ID, -description, and other <>. [float] [[osquery-view-history]] @@ -72,17 +71,17 @@ Each query has the following options: [role="screenshot"] image::images/live-query-check-results.png[Results of OSquery] - [float] [[osquery-schedule-query]] == Schedule queries with packs -Create packs to organize sets of queries. For example, you might create one pack that checks -for IT compliance-type issues, and another pack that monitors for evidence of malware. -You can schedule packs to run for one or more agent policies. When scheduled, queries in the pack are run at the set intervals for all agents in those policies. Scheduling packs is optional. +A pack is a set of grouped queries that perform similar functions or address common use cases. <> are available to download and can help you get started using the Osquery integration. -. Open the **Packs** tab. +You can also create a custom pack with one or more queries. For example, when creating custom packs, you might create one pack that checks for IT compliance-type issues, and another pack that monitors for evidence of malware. +You can run packs as live queries or schedule packs to run for one or more agent policies. When scheduled, queries in the pack are run at the set intervals for all agents in those policies. + +. Click the **Packs** tab. . Click **Add pack** to create a new pack, or click the name of an existing pack, then **Edit** to add queries to an existing pack. . Provide the following fields: @@ -91,7 +90,7 @@ You can schedule packs to run for one or more agent policies. When scheduled, qu * A short description of the pack. -* The agent policies where this pack should run. If no agent policies are set, then the pack is not scheduled. +* The agent policies where this pack should run. If no agent policies are set, the pack is not scheduled. . Add queries to schedule: @@ -159,28 +158,13 @@ Once you save a query, you can only edit it from the *Saved queries* tab: [float] [[osquery-prebuilt-packs-queries]] == Prebuilt Elastic packs and queries -Osquery Manager includes a set of prebuilt Osquery packs and saved queries -that can help you get started using the integration. - -[float] -[[osquery-prebuilt-queries]] -=== Prebuilt queries -A set of saved queries are included with the integration and available to run as a live query. -Note the following about the prebuilt queries: - -* The queries are not editable. - -* Several of the queries include default ECS mappings to standardize the results. - -* The prebuilt Elastic queries all follow the same naming convention and identify -what type of information is being queried, what operating system it supports if it's limited to one or more, -and that these are Elastic queries. For example, `firewall_rules_windows_elastic`. +The prebuilt Osquery packs are included with the integration. Once you add a pack, you can activate and schedule it. [float] [[osquery-prebuilt-packs]] === Prebuilt packs -The prebuilt Osquery packs are included with the integration and can be optionally loaded. -Once added, you can then activate and schedule the packs. +The prebuilt Osquery packs are included with the integration and can be optionally loaded. +Once added, you can then activate and schedule the packs. You can modify the scheduled agent policies for a prebuilt pack, but you cannot edit queries in the pack. To edit the queries, you must first create a copy of the pack. @@ -194,7 +178,7 @@ For information about the prebuilt packs that are available, refer to < +{{/context.hits}} +-------------------------------------------------- +// NOTCONSOLE +<1> The `fields` parameter here is used to access the `day_of_week` runtime field. +-- ++ +As the {ref}/search-fields.html#search-fields-response[`fields`] response always returns an array of values for each field, +the https://mustache.github.io/[Mustache] template array syntax is used to iterate over these values in your actions as the following example shows: ++ +-- +[source] +-------------------------------------------------- +{{#context.hits}} +Labels: +{{#fields.labels}} +- {{.}} +{{/fields.labels}} +{{/context.hits}} +-------------------------------------------------- +// NOTCONSOLE +-- [float] ==== Test your query diff --git a/package.json b/package.json index 637aa2a26158b..c0effd409918a 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,9 @@ "**/refractor/prismjs": "~1.27.0", "**/trim": "1.0.1", "**/typescript": "4.6.3", + "**/use-composed-ref": "^1.3.0", + "**/use-latest": "^1.2.1", + "@tanstack/query-core": "^4.2.1", "globby/fast-glob": "3.2.7", "puppeteer/node-fetch": "^2.6.7" }, @@ -234,11 +237,14 @@ "@kbn/core-preboot-server-mocks": "link:bazel-bin/packages/core/preboot/core-preboot-server-mocks", "@kbn/core-saved-objects-api-browser": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-api-browser", "@kbn/core-saved-objects-api-server": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-api-server", + "@kbn/core-saved-objects-base-server-internal": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-base-server-internal", + "@kbn/core-saved-objects-base-server-mocks": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-base-server-mocks", "@kbn/core-saved-objects-browser": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-browser", "@kbn/core-saved-objects-browser-internal": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-browser-internal", "@kbn/core-saved-objects-browser-mocks": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-browser-mocks", "@kbn/core-saved-objects-common": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-common", "@kbn/core-saved-objects-server": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-server", + "@kbn/core-saved-objects-utils-server": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-utils-server", "@kbn/core-test-helpers-deprecations-getters": "link:bazel-bin/packages/core/test-helpers/core-test-helpers-deprecations-getters", "@kbn/core-test-helpers-http-setup-browser": "link:bazel-bin/packages/core/test-helpers/core-test-helpers-http-setup-browser", "@kbn/core-theme-browser": "link:bazel-bin/packages/core/theme/core-theme-browser", @@ -360,6 +366,8 @@ "@opentelemetry/semantic-conventions": "^1.4.0", "@reduxjs/toolkit": "^1.6.1", "@slack/webhook": "^5.0.4", + "@tanstack/react-query": "^4.2.1", + "@tanstack/react-query-devtools": "^4.2.1", "@turf/along": "6.0.1", "@turf/area": "6.0.1", "@turf/bbox": "6.0.1", @@ -387,7 +395,7 @@ "color": "^4.2.3", "commander": "^4.1.1", "compare-versions": "3.5.1", - "constate": "^1.3.2", + "constate": "^3.3.2", "content-disposition": "^0.5.4", "copy-to-clipboard": "^3.0.8", "core-js": "^3.23.5", @@ -496,7 +504,7 @@ "pluralize": "3.1.0", "polished": "^3.7.2", "pretty-ms": "6.0.0", - "prop-types": "^15.7.2", + "prop-types": "^15.8.1", "proxy-from-env": "1.0.0", "puid": "1.0.7", "puppeteer": "^10.2.0", @@ -504,39 +512,38 @@ "rbush": "^3.0.1", "re-resizable": "^6.1.1", "re2": "1.17.4", - "react": "^16.14.0", + "react": "^17.0.2", "react-ace": "^7.0.5", "react-beautiful-dnd": "^13.1.0", "react-color": "^2.13.8", - "react-dom": "^16.14.0", + "react-dom": "^17.0.2", "react-dropzone": "^4.2.9", "react-fast-compare": "^2.0.4", - "react-grid-layout": "^0.16.2", - "react-hook-form": "^7.30.0", + "react-focus-on": "^3.6.0", + "react-grid-layout": "^1.3.4", + "react-hook-form": "^7.34.2", "react-intl": "^2.8.0", - "react-is": "^16.13.1", - "react-markdown": "^4.3.1", + "react-is": "^17.0.2", + "react-markdown": "^6.0.3", "react-moment-proptypes": "^1.7.0", "react-monaco-editor": "^0.41.2", - "react-popper-tooltip": "^2.10.1", - "react-query": "^3.39.1", - "react-redux": "^7.2.0", - "react-resizable": "^1.7.5", - "react-resize-detector": "^4.2.0", - "react-reverse-portal": "^1.0.4", + "react-popper-tooltip": "^3.1.1", + "react-redux": "^7.2.8", + "react-resizable": "^3.0.4", + "react-resize-detector": "^7.1.1", + "react-reverse-portal": "^2.1.0", "react-router": "^5.2.0", "react-router-config": "^5.1.1", "react-router-dom": "^5.2.0", - "react-router-redux": "^4.0.8", - "react-shortcuts": "^2.0.0", - "react-sizeme": "^2.6.6", + "react-shortcuts": "^2.1.0", + "react-sizeme": "^3.0.2", "react-syntax-highlighter": "^15.3.1", "react-tiny-virtual-list": "^2.2.0", "react-use": "^15.3.8", - "react-virtualized": "^9.21.2", + "react-virtualized": "^9.22.3", "react-vis": "^1.8.1", "react-visibility-sensor": "^5.1.1", - "recompose": "^0.26.0", + "recompose": "^0.30.0", "reduce-reducers": "^1.0.4", "redux": "^4.1.0", "redux-actions": "^2.6.5", @@ -692,11 +699,13 @@ "@storybook/addon-storyshots": "^6.4.22", "@storybook/addons": "^6.4.22", "@storybook/api": "^6.4.22", + "@storybook/client-api": "^6.4.22", "@storybook/components": "^6.4.22", "@storybook/core": "^6.4.22", "@storybook/core-common": "^6.4.22", "@storybook/core-events": "^6.4.22", "@storybook/node-logger": "^6.4.22", + "@storybook/preview-web": "^6.4.22", "@storybook/react": "^6.4.22", "@storybook/testing-react": "^1.2.4", "@storybook/theming": "^6.4.22", @@ -732,7 +741,7 @@ "@types/deep-freeze-strict": "^1.1.0", "@types/delete-empty": "^2.0.0", "@types/ejs": "^3.0.6", - "@types/enzyme": "^3.10.8", + "@types/enzyme": "^3.10.12", "@types/eslint": "^7.28.0", "@types/express": "^4.17.13", "@types/extract-zip": "^1.6.2", @@ -759,7 +768,6 @@ "@types/intl-relativeformat": "^2.1.0", "@types/jest": "^26.0.22", "@types/jest-axe": "^3.5.3", - "@types/jest-specific-snapshot": "^0.5.5", "@types/joi": "^17.2.3", "@types/jquery": "^3.3.31", "@types/js-levenshtein": "^1.1.0", @@ -889,11 +897,14 @@ "@types/kbn__core-public-internal-base": "link:bazel-bin/packages/core/public/internal-base/npm_module_types", "@types/kbn__core-saved-objects-api-browser": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-api-browser/npm_module_types", "@types/kbn__core-saved-objects-api-server": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-api-server/npm_module_types", + "@types/kbn__core-saved-objects-base-server-internal": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-base-server-internal/npm_module_types", + "@types/kbn__core-saved-objects-base-server-mocks": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-base-server-mocks/npm_module_types", "@types/kbn__core-saved-objects-browser": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-browser/npm_module_types", "@types/kbn__core-saved-objects-browser-internal": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-browser-internal/npm_module_types", "@types/kbn__core-saved-objects-browser-mocks": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-browser-mocks/npm_module_types", "@types/kbn__core-saved-objects-common": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-common/npm_module_types", "@types/kbn__core-saved-objects-server": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-server/npm_module_types", + "@types/kbn__core-saved-objects-utils-server": "link:bazel-bin/packages/core/saved-objects/core-saved-objects-utils-server/npm_module_types", "@types/kbn__core-server-internal-base": "link:bazel-bin/packages/core/server/internal-base/npm_module_types", "@types/kbn__core-test-helpers-deprecations-getters": "link:bazel-bin/packages/core/test-helpers/core-test-helpers-deprecations-getters/npm_module_types", "@types/kbn__core-test-helpers-http-setup-browser": "link:bazel-bin/packages/core/test-helpers/core-test-helpers-http-setup-browser/npm_module_types", @@ -1063,23 +1074,22 @@ "@types/pngjs": "^3.4.0", "@types/prettier": "^2.3.2", "@types/pretty-ms": "^5.0.0", - "@types/prop-types": "^15.7.3", + "@types/prop-types": "^15.7.5", "@types/rbush": "^3.0.0", - "@types/react": "^16.14.25", + "@types/react": "^17.0.45", "@types/react-beautiful-dnd": "^13.0.0", - "@types/react-dom": "^16.9.15", - "@types/react-grid-layout": "^0.16.7", + "@types/react-dom": "^17.0.17", + "@types/react-grid-layout": "^1.3.2", "@types/react-intl": "^2.3.15", - "@types/react-is": "^16.7.2", - "@types/react-redux": "^7.1.9", - "@types/react-resize-detector": "^4.0.1", + "@types/react-is": "^17.0.3", + "@types/react-resize-detector": "^6.1.0", "@types/react-router": "^5.1.7", "@types/react-router-config": "^5.0.2", "@types/react-router-dom": "^5.1.5", - "@types/react-test-renderer": "^16.9.1", - "@types/react-virtualized": "^9.18.7", + "@types/react-test-renderer": "^17.0.2", + "@types/react-virtualized": "^9.21.21", "@types/react-vis": "^1.11.9", - "@types/recompose": "^0.30.6", + "@types/recompose": "^0.30.10", "@types/reduce-reducers": "^1.0.0", "@types/redux-actions": "^2.6.1", "@types/redux-logger": "^3.0.8", @@ -1119,6 +1129,7 @@ "@typescript-eslint/eslint-plugin": "^5.20.0", "@typescript-eslint/parser": "^5.20.0", "@typescript-eslint/typescript-estree": "^5.20.0", + "@wojtekmaj/enzyme-adapter-react-17": "^0.6.7", "@yarnpkg/lockfile": "^1.1.0", "abab": "^2.0.4", "aggregate-error": "^3.1.0", @@ -1164,8 +1175,6 @@ "dpdm": "3.5.0", "ejs": "^3.1.8", "enzyme": "^3.11.0", - "enzyme-adapter-react-16": "^1.15.6", - "enzyme-adapter-utils": "^1.14.0", "enzyme-to-json": "^3.6.1", "eslint": "^7.32.0", "eslint-config-prettier": "^8.5.0", @@ -1213,16 +1222,15 @@ "jest-config": "^26", "jest-diff": "^26.6.2", "jest-environment-jsdom": "^26.6.2", - "jest-environment-jsdom-thirteen": "^1.0.1", "jest-mock": "^26.6.2", "jest-raw-loader": "^1.0.1", "jest-runtime": "^26", "jest-silent-reporter": "^0.5.0", "jest-snapshot": "^26.6.2", - "jest-specific-snapshot": "2.0.0", + "jest-specific-snapshot": "^4.0.0", "jest-styled-components": "^7.0.3", "jimp": "^0.14.0", - "jsdom": "13.1.0", + "jsdom": "^16.4.0", "json-schema-typed": "^8.0.1", "json5": "^1.0.1", "jsondiffpatch": "0.4.1", @@ -1231,7 +1239,7 @@ "lmdb-store": "^1.6.11", "loader-utils": "^1.2.3", "marge": "^1.0.1", - "micromatch": "3.1.10", + "micromatch": "^4.0.5", "mini-css-extract-plugin": "1.1.0", "minimist": "^1.2.6", "mocha": "^10.0.0", @@ -1262,10 +1270,10 @@ "proxy": "^1.0.2", "q": "^1.5.1", "raw-loader": "^3.1.0", - "react-test-renderer": "^16.14.0", + "react-test-renderer": "^17.0.2", "regenerate": "^1.4.0", "resolve": "^1.22.0", - "rxjs-marbles": "^5.0.6", + "rxjs-marbles": "^7.0.1", "sass-loader": "^10.3.1", "selenium-webdriver": "^4.4.0", "simple-git": "^3.10.0", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index a9a0377b679dd..b084fa8617929 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -102,11 +102,14 @@ filegroup( "//packages/core/preboot/core-preboot-server:build", "//packages/core/saved-objects/core-saved-objects-api-browser:build", "//packages/core/saved-objects/core-saved-objects-api-server:build", + "//packages/core/saved-objects/core-saved-objects-base-server-internal:build", + "//packages/core/saved-objects/core-saved-objects-base-server-mocks:build", "//packages/core/saved-objects/core-saved-objects-browser-internal:build", "//packages/core/saved-objects/core-saved-objects-browser-mocks:build", "//packages/core/saved-objects/core-saved-objects-browser:build", "//packages/core/saved-objects/core-saved-objects-common:build", "//packages/core/saved-objects/core-saved-objects-server:build", + "//packages/core/saved-objects/core-saved-objects-utils-server:build", "//packages/core/test-helpers/core-test-helpers-deprecations-getters:build", "//packages/core/test-helpers/core-test-helpers-http-setup-browser:build", "//packages/core/theme/core-theme-browser-internal:build", @@ -375,11 +378,14 @@ filegroup( "//packages/core/preboot/core-preboot-server:build_types", "//packages/core/saved-objects/core-saved-objects-api-browser:build_types", "//packages/core/saved-objects/core-saved-objects-api-server:build_types", + "//packages/core/saved-objects/core-saved-objects-base-server-internal:build_types", + "//packages/core/saved-objects/core-saved-objects-base-server-mocks:build_types", "//packages/core/saved-objects/core-saved-objects-browser-internal:build_types", "//packages/core/saved-objects/core-saved-objects-browser-mocks:build_types", "//packages/core/saved-objects/core-saved-objects-browser:build_types", "//packages/core/saved-objects/core-saved-objects-common:build_types", "//packages/core/saved-objects/core-saved-objects-server:build_types", + "//packages/core/saved-objects/core-saved-objects-utils-server:build_types", "//packages/core/test-helpers/core-test-helpers-deprecations-getters:build_types", "//packages/core/test-helpers/core-test-helpers-http-setup-browser:build_types", "//packages/core/theme/core-theme-browser-internal:build_types", diff --git a/packages/core/overlays/core-overlays-browser-internal/src/banners/user_banner_service.tsx b/packages/core/overlays/core-overlays-browser-internal/src/banners/user_banner_service.tsx index 81ee879615b76..5fdc4439136f9 100644 --- a/packages/core/overlays/core-overlays-browser-internal/src/banners/user_banner_service.tsx +++ b/packages/core/overlays/core-overlays-browser-internal/src/banners/user_banner_service.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { Fragment } from 'react'; +import React from 'react'; import ReactDOM from 'react-dom'; import { filter } from 'rxjs/operators'; import { Subscription } from 'rxjs'; @@ -72,7 +72,7 @@ export class UserBannerService { } > - + {content.trim()} banners.remove(id!)}> diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/BUILD.bazel b/packages/core/saved-objects/core-saved-objects-base-server-internal/BUILD.bazel new file mode 100644 index 0000000000000..d64f0bb533a0f --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/BUILD.bazel @@ -0,0 +1,111 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "core-saved-objects-base-server-internal" +PKG_REQUIRE_NAME = "@kbn/core-saved-objects-base-server-internal" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = [ + "**/*.test.*", + "**/*.stories.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +RUNTIME_DEPS = [ + "@npm//lodash", + "@npm//semver", + "//packages/kbn-config-schema", + ### test dependencies + "//packages/kbn-logging-mocks", + "@npm//@hapi/boom", +] + +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "@npm//@types/lodash", + "@npm//@types/semver", + "//packages/kbn-logging:npm_module_types", + "//packages/kbn-config-schema:npm_module_types", + "//packages/core/base/core-base-server-internal:npm_module_types", + "//packages/core/saved-objects/core-saved-objects-server:npm_module_types", + "//packages/core/saved-objects/core-saved-objects-utils-server:npm_module_types", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), + root_input_dir = "src", +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + declaration_map = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/README.md b/packages/core/saved-objects/core-saved-objects-base-server-internal/README.md new file mode 100644 index 0000000000000..0315d68aca7b0 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/README.md @@ -0,0 +1,4 @@ +# @kbn/core-saved-objects-base-server-internal + +This package contains the base parts of the server-side savedObjects internal implementation, +used by all the other internal server-side savedObjects packages. \ No newline at end of file diff --git a/src/plugins/controls/public/control_types/range_slider/range_slider.component.tsx b/packages/core/saved-objects/core-saved-objects-base-server-internal/jest.config.js similarity index 64% rename from src/plugins/controls/public/control_types/range_slider/range_slider.component.tsx rename to packages/core/saved-objects/core-saved-objects-base-server-internal/jest.config.js index ca9c1e4b2dd0c..384625b895fcd 100644 --- a/src/plugins/controls/public/control_types/range_slider/range_slider.component.tsx +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/jest.config.js @@ -6,12 +6,8 @@ * Side Public License, v 1. */ -import React from 'react'; - -import { RangeSliderPopover } from './range_slider_popover'; - -import './range_slider.scss'; - -export const RangeSliderComponent = () => { - return ; +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../../../..', + roots: ['/packages/core/saved-objects/core-saved-objects-base-server-internal'], }; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/package.json b/packages/core/saved-objects/core-saved-objects-base-server-internal/package.json new file mode 100644 index 0000000000000..ff4b901f49056 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/package.json @@ -0,0 +1,7 @@ +{ + "name": "@kbn/core-saved-objects-base-server-internal", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/index.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/index.ts new file mode 100644 index 0000000000000..5b504f43244d6 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/index.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { LEGACY_URL_ALIAS_TYPE, type LegacyUrlAlias } from './legacy_alias'; +export { + getProperty, + getRootProperties, + getRootPropertiesObjects, + getTypes, + type IndexMapping, + type IndexMappingMeta, + type SavedObjectsTypeMappingDefinitions, +} from './mappings'; +export { SavedObjectsSerializer } from './serialization'; +export { SavedObjectsTypeValidator } from './validation'; +export { decodeRequestVersion, decodeVersion, encodeVersion, encodeHitVersion } from './version'; +export { + savedObjectsConfig, + savedObjectsMigrationConfig, + SavedObjectConfig, + type SavedObjectsConfigType, + type SavedObjectsMigrationConfigType, +} from './saved_objects_config'; +export { SavedObjectTypeRegistry } from './saved_objects_type_registry'; diff --git a/src/core/server/saved_objects/object_types/constants.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/legacy_alias/constants.ts similarity index 100% rename from src/core/server/saved_objects/object_types/constants.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/legacy_alias/constants.ts diff --git a/src/core/server/saved_objects/version/index.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/legacy_alias/index.ts similarity index 70% rename from src/core/server/saved_objects/version/index.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/legacy_alias/index.ts index 715073fb98e7d..f782267bd2096 100644 --- a/src/core/server/saved_objects/version/index.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/legacy_alias/index.ts @@ -6,7 +6,5 @@ * Side Public License, v 1. */ -export * from './encode_version'; -export * from './encode_hit_version'; -export * from './decode_version'; -export * from './decode_request_version'; +export type { LegacyUrlAlias } from './types'; +export { LEGACY_URL_ALIAS_TYPE } from './constants'; diff --git a/src/core/server/saved_objects/object_types/types.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/legacy_alias/types.ts similarity index 100% rename from src/core/server/saved_objects/object_types/types.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/legacy_alias/types.ts diff --git a/src/core/server/saved_objects/mappings/index.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/index.ts similarity index 100% rename from src/core/server/saved_objects/mappings/index.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/index.ts diff --git a/src/core/server/saved_objects/mappings/lib/get_property.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/lib/get_property.test.ts similarity index 100% rename from src/core/server/saved_objects/mappings/lib/get_property.test.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/lib/get_property.test.ts diff --git a/src/core/server/saved_objects/mappings/lib/get_property.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/lib/get_property.ts similarity index 100% rename from src/core/server/saved_objects/mappings/lib/get_property.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/lib/get_property.ts diff --git a/src/core/server/saved_objects/mappings/lib/get_root_properties.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/lib/get_root_properties.ts similarity index 100% rename from src/core/server/saved_objects/mappings/lib/get_root_properties.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/lib/get_root_properties.ts diff --git a/src/core/server/saved_objects/mappings/lib/get_root_properties_objects.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/lib/get_root_properties_objects.test.ts similarity index 100% rename from src/core/server/saved_objects/mappings/lib/get_root_properties_objects.test.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/lib/get_root_properties_objects.test.ts diff --git a/src/core/server/saved_objects/mappings/lib/get_root_properties_objects.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/lib/get_root_properties_objects.ts similarity index 100% rename from src/core/server/saved_objects/mappings/lib/get_root_properties_objects.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/lib/get_root_properties_objects.ts diff --git a/src/core/server/saved_objects/mappings/lib/get_types.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/lib/get_types.ts similarity index 100% rename from src/core/server/saved_objects/mappings/lib/get_types.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/lib/get_types.ts diff --git a/src/core/server/saved_objects/mappings/lib/index.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/lib/index.ts similarity index 100% rename from src/core/server/saved_objects/mappings/lib/index.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/lib/index.ts diff --git a/src/core/server/saved_objects/mappings/types.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/types.ts similarity index 100% rename from src/core/server/saved_objects/mappings/types.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/mappings/types.ts diff --git a/src/core/server/saved_objects/saved_objects_config.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_config.ts similarity index 100% rename from src/core/server/saved_objects/saved_objects_config.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_config.ts diff --git a/src/core/server/saved_objects/saved_objects_type_registry.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_type_registry.test.ts similarity index 100% rename from src/core/server/saved_objects/saved_objects_type_registry.test.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_type_registry.test.ts diff --git a/src/core/server/saved_objects/saved_objects_type_registry.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_type_registry.ts similarity index 100% rename from src/core/server/saved_objects/saved_objects_type_registry.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/saved_objects_type_registry.ts diff --git a/src/core/server/saved_objects/serialization/index.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/serialization/index.ts similarity index 100% rename from src/core/server/saved_objects/serialization/index.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/serialization/index.ts diff --git a/src/core/server/saved_objects/serialization/serializer.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/serialization/serializer.test.ts similarity index 96% rename from src/core/server/saved_objects/serialization/serializer.test.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/serialization/serializer.test.ts index e0ef357c0d9be..230993bd06d29 100644 --- a/src/core/server/saved_objects/serialization/serializer.test.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/serialization/serializer.test.ts @@ -7,28 +7,47 @@ */ import _ from 'lodash'; -import type { SavedObjectsRawDoc } from '@kbn/core-saved-objects-server'; +import type { SavedObjectsRawDoc, ISavedObjectTypeRegistry } from '@kbn/core-saved-objects-server'; import { SavedObjectsSerializer } from './serializer'; -import { typeRegistryMock } from '../saved_objects_type_registry.mock'; import { encodeVersion } from '../version'; -import { LEGACY_URL_ALIAS_TYPE } from '../object_types'; +import { LEGACY_URL_ALIAS_TYPE } from '../legacy_alias'; + +const createMockedTypeRegistry = ({ + isNamespaceAgnostic, + isSingleNamespace, + isMultiNamespace, +}: { + isNamespaceAgnostic: boolean; + isSingleNamespace: boolean; + isMultiNamespace: boolean; +}): ISavedObjectTypeRegistry => { + const typeRegistry: Partial = { + isNamespaceAgnostic: jest.fn().mockReturnValue(isNamespaceAgnostic), + isSingleNamespace: jest.fn().mockReturnValue(isSingleNamespace), + isMultiNamespace: jest.fn().mockReturnValue(isMultiNamespace), + }; + return typeRegistry as ISavedObjectTypeRegistry; +}; -let typeRegistry = typeRegistryMock.create(); -typeRegistry.isNamespaceAgnostic.mockReturnValue(true); -typeRegistry.isSingleNamespace.mockReturnValue(false); -typeRegistry.isMultiNamespace.mockReturnValue(false); +let typeRegistry = createMockedTypeRegistry({ + isNamespaceAgnostic: true, + isSingleNamespace: false, + isMultiNamespace: false, +}); const namespaceAgnosticSerializer = new SavedObjectsSerializer(typeRegistry); -typeRegistry = typeRegistryMock.create(); -typeRegistry.isNamespaceAgnostic.mockReturnValue(false); -typeRegistry.isSingleNamespace.mockReturnValue(true); -typeRegistry.isMultiNamespace.mockReturnValue(false); +typeRegistry = typeRegistry = createMockedTypeRegistry({ + isNamespaceAgnostic: false, + isSingleNamespace: true, + isMultiNamespace: false, +}); const singleNamespaceSerializer = new SavedObjectsSerializer(typeRegistry); -typeRegistry = typeRegistryMock.create(); -typeRegistry.isNamespaceAgnostic.mockReturnValue(false); -typeRegistry.isSingleNamespace.mockReturnValue(false); -typeRegistry.isMultiNamespace.mockReturnValue(true); +typeRegistry = typeRegistry = createMockedTypeRegistry({ + isNamespaceAgnostic: false, + isSingleNamespace: false, + isMultiNamespace: true, +}); const multiNamespaceSerializer = new SavedObjectsSerializer(typeRegistry); const sampleTemplate = { diff --git a/src/core/server/saved_objects/serialization/serializer.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/serialization/serializer.ts similarity index 98% rename from src/core/server/saved_objects/serialization/serializer.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/serialization/serializer.ts index 2e13708ee2f9d..340926abd0bce 100644 --- a/src/core/server/saved_objects/serialization/serializer.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/serialization/serializer.ts @@ -14,9 +14,9 @@ import type { SavedObjectSanitizedDoc, SavedObjectsRawDocParseOptions, } from '@kbn/core-saved-objects-server'; -import { LEGACY_URL_ALIAS_TYPE } from '../object_types'; +import { SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server'; +import { LEGACY_URL_ALIAS_TYPE } from '../legacy_alias'; import { decodeVersion, encodeVersion } from '../version'; -import { SavedObjectsUtils } from '../service'; /** * Core internal implementation of {@link ISavedObjectsSerializer} diff --git a/src/core/server/saved_objects/validation/index.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/validation/index.ts similarity index 100% rename from src/core/server/saved_objects/validation/index.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/validation/index.ts diff --git a/src/core/server/saved_objects/validation/schema.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/validation/schema.test.ts similarity index 100% rename from src/core/server/saved_objects/validation/schema.test.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/validation/schema.test.ts diff --git a/src/core/server/saved_objects/validation/schema.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/validation/schema.ts similarity index 97% rename from src/core/server/saved_objects/validation/schema.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/validation/schema.ts index 8e90c4aef9c70..221f21b5aa992 100644 --- a/src/core/server/saved_objects/validation/schema.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/validation/schema.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { schema, Type } from '@kbn/config-schema'; +import { schema, type Type } from '@kbn/config-schema'; import type { SavedObjectsValidationSpec, SavedObjectSanitizedDoc, diff --git a/src/core/server/saved_objects/validation/validator.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/validation/validator.test.ts similarity index 95% rename from src/core/server/saved_objects/validation/validator.test.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/validation/validator.test.ts index b059070fdf75f..96bc93be54c1a 100644 --- a/src/core/server/saved_objects/validation/validator.test.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/validation/validator.test.ts @@ -7,12 +7,12 @@ */ import { schema } from '@kbn/config-schema'; +import { loggerMock, type MockedLogger } from '@kbn/logging-mocks'; import type { SavedObjectSanitizedDoc, SavedObjectsValidationMap, } from '@kbn/core-saved-objects-server'; -import { SavedObjectsTypeValidator } from '.'; -import { loggerMock, MockedLogger } from '@kbn/logging-mocks'; +import { SavedObjectsTypeValidator } from './validator'; describe('Saved Objects type validator', () => { let validator: SavedObjectsTypeValidator; diff --git a/src/core/server/saved_objects/validation/validator.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/validation/validator.ts similarity index 100% rename from src/core/server/saved_objects/validation/validator.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/validation/validator.ts diff --git a/src/core/server/saved_objects/version/base64.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/base64.ts similarity index 100% rename from src/core/server/saved_objects/version/base64.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/base64.ts diff --git a/src/core/server/saved_objects/version/decode_request_version.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/decode_request_version.test.ts similarity index 100% rename from src/core/server/saved_objects/version/decode_request_version.test.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/decode_request_version.test.ts diff --git a/src/core/server/saved_objects/version/decode_request_version.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/decode_request_version.ts similarity index 100% rename from src/core/server/saved_objects/version/decode_request_version.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/decode_request_version.ts diff --git a/src/core/server/saved_objects/version/decode_version.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/decode_version.test.ts similarity index 100% rename from src/core/server/saved_objects/version/decode_version.test.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/decode_version.test.ts diff --git a/src/core/server/saved_objects/version/decode_version.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/decode_version.ts similarity index 93% rename from src/core/server/saved_objects/version/decode_version.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/decode_version.ts index 4166e7eef06c7..dd4bdcdc8186f 100644 --- a/src/core/server/saved_objects/version/decode_version.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/decode_version.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { SavedObjectsErrorHelpers } from '../service/lib/errors'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; import { decodeBase64 } from './base64'; /** diff --git a/src/core/server/saved_objects/version/encode_hit_version.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/encode_hit_version.test.ts similarity index 100% rename from src/core/server/saved_objects/version/encode_hit_version.test.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/encode_hit_version.test.ts diff --git a/src/core/server/saved_objects/version/encode_hit_version.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/encode_hit_version.ts similarity index 100% rename from src/core/server/saved_objects/version/encode_hit_version.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/encode_hit_version.ts diff --git a/src/core/server/saved_objects/version/encode_version.test.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/encode_version.test.ts similarity index 100% rename from src/core/server/saved_objects/version/encode_version.test.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/encode_version.test.ts diff --git a/src/core/server/saved_objects/version/encode_version.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/encode_version.ts similarity index 100% rename from src/core/server/saved_objects/version/encode_version.ts rename to packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/encode_version.ts diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/index.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/index.ts new file mode 100644 index 0000000000000..2fb31940d4408 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/version/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { encodeVersion } from './encode_version'; +export { encodeHitVersion } from './encode_hit_version'; +export { decodeVersion } from './decode_version'; +export { decodeRequestVersion } from './decode_request_version'; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/tsconfig.json b/packages/core/saved-objects/core-saved-objects-base-server-internal/tsconfig.json new file mode 100644 index 0000000000000..39d3c7097814a --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/core/saved-objects/core-saved-objects-base-server-mocks/BUILD.bazel b/packages/core/saved-objects/core-saved-objects-base-server-mocks/BUILD.bazel new file mode 100644 index 0000000000000..9f3538c20a752 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-mocks/BUILD.bazel @@ -0,0 +1,100 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "core-saved-objects-base-server-mocks" +PKG_REQUIRE_NAME = "@kbn/core-saved-objects-base-server-mocks" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = [ + "**/*.test.*", + "**/*.stories.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +RUNTIME_DEPS = [ +] + +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "//packages/core/saved-objects/core-saved-objects-server:npm_module_types", + "//packages/core/saved-objects/core-saved-objects-base-server-internal:npm_module_types", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), + root_input_dir = "src", +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + declaration_map = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/core/saved-objects/core-saved-objects-base-server-mocks/README.md b/packages/core/saved-objects/core-saved-objects-base-server-mocks/README.md new file mode 100644 index 0000000000000..5da5264ce9eed --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-mocks/README.md @@ -0,0 +1,5 @@ +# @kbn/core-saved-objects-base-server-mocks + +This package contains the mocks for the base server-side savedObjects sub-domain: +- `SavedObjectTypeRegistry` mock +- `SavedObjectsSerializer` mock diff --git a/packages/core/saved-objects/core-saved-objects-base-server-mocks/jest.config.js b/packages/core/saved-objects/core-saved-objects-base-server-mocks/jest.config.js new file mode 100644 index 0000000000000..fee774e787693 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-mocks/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../../../..', + roots: ['/packages/core/saved-objects/core-saved-objects-base-server-mocks'], +}; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-mocks/package.json b/packages/core/saved-objects/core-saved-objects-base-server-mocks/package.json new file mode 100644 index 0000000000000..e119e704c7ace --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-mocks/package.json @@ -0,0 +1,7 @@ +{ + "name": "@kbn/core-saved-objects-base-server-mocks", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/src/plugins/discover/public/components/doc_table/index.ts b/packages/core/saved-objects/core-saved-objects-base-server-mocks/src/index.ts similarity index 64% rename from src/plugins/discover/public/components/doc_table/index.ts rename to packages/core/saved-objects/core-saved-objects-base-server-mocks/src/index.ts index e79276b0c9687..62657cf81ea61 100644 --- a/src/plugins/discover/public/components/doc_table/index.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-mocks/src/index.ts @@ -6,6 +6,5 @@ * Side Public License, v 1. */ -export { getSort, getSortArray } from './utils/get_sort'; -export { getSortForSearchSource } from './utils/get_sort_for_search_source'; -export { getDefaultSort } from './utils/get_default_sort'; +export { typeRegistryMock } from './saved_objects_type_registry.mock'; +export { serializerMock } from './serializer.mock'; diff --git a/src/core/server/saved_objects/saved_objects_type_registry.mock.ts b/packages/core/saved-objects/core-saved-objects-base-server-mocks/src/saved_objects_type_registry.mock.ts similarity index 94% rename from src/core/server/saved_objects/saved_objects_type_registry.mock.ts rename to packages/core/saved-objects/core-saved-objects-base-server-mocks/src/saved_objects_type_registry.mock.ts index 70d05a0f7b0ab..8f792f177b5fc 100644 --- a/src/core/server/saved_objects/saved_objects_type_registry.mock.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-mocks/src/saved_objects_type_registry.mock.ts @@ -7,7 +7,7 @@ */ import type { ISavedObjectTypeRegistry } from '@kbn/core-saved-objects-server'; -import type { SavedObjectTypeRegistry } from './saved_objects_type_registry'; +import type { SavedObjectTypeRegistry } from '@kbn/core-saved-objects-base-server-internal'; const createRegistryMock = (): jest.Mocked< ISavedObjectTypeRegistry & Pick diff --git a/packages/core/saved-objects/core-saved-objects-base-server-mocks/src/serializer.mock.ts b/packages/core/saved-objects/core-saved-objects-base-server-mocks/src/serializer.mock.ts new file mode 100644 index 0000000000000..6bdac2e20c1f9 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-mocks/src/serializer.mock.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { ISavedObjectsSerializer } from '@kbn/core-saved-objects-server'; + +const createSerializerMock = () => { + const mock: jest.Mocked = { + isRawSavedObject: jest.fn(), + rawToSavedObject: jest.fn(), + savedObjectToRaw: jest.fn(), + generateRawId: jest.fn(), + generateRawLegacyUrlAliasId: jest.fn(), + }; + return mock; +}; + +export const serializerMock = { + create: createSerializerMock, +}; diff --git a/packages/core/saved-objects/core-saved-objects-base-server-mocks/tsconfig.json b/packages/core/saved-objects/core-saved-objects-base-server-mocks/tsconfig.json new file mode 100644 index 0000000000000..39d3c7097814a --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-base-server-mocks/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/core/saved-objects/core-saved-objects-utils-server/BUILD.bazel b/packages/core/saved-objects/core-saved-objects-utils-server/BUILD.bazel new file mode 100644 index 0000000000000..71a61799ee46a --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-utils-server/BUILD.bazel @@ -0,0 +1,106 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "core-saved-objects-utils-server" +PKG_REQUIRE_NAME = "@kbn/core-saved-objects-utils-server" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = [ + "**/*.test.*", + "**/*.stories.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +RUNTIME_DEPS = [ + "@npm//lodash", + "@npm//uuid", + "@npm//@hapi/boom", +] + + +TYPES_DEPS = [ + "@npm//@types/node", + "@npm//@types/jest", + "@npm//@types/lodash", + "@npm//@types/uuid", + "@npm//@hapi/boom", + "//packages/core/saved-objects/core-saved-objects-server:npm_module_types", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), + root_input_dir = "src", +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + declaration_map = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/core/saved-objects/core-saved-objects-utils-server/README.md b/packages/core/saved-objects/core-saved-objects-utils-server/README.md new file mode 100644 index 0000000000000..ecbfa469575ae --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-utils-server/README.md @@ -0,0 +1,5 @@ +# @kbn/core-saved-objects-utils-server + +This package contains public utilities for Core's server-side `savedObjects` domain. + + diff --git a/packages/core/saved-objects/core-saved-objects-utils-server/jest.config.js b/packages/core/saved-objects/core-saved-objects-utils-server/jest.config.js new file mode 100644 index 0000000000000..5458bf05b6767 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-utils-server/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../../../..', + roots: ['/packages/core/saved-objects/core-saved-objects-utils-server'], +}; diff --git a/packages/core/saved-objects/core-saved-objects-utils-server/package.json b/packages/core/saved-objects/core-saved-objects-utils-server/package.json new file mode 100644 index 0000000000000..13220da373261 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-utils-server/package.json @@ -0,0 +1,7 @@ +{ + "name": "@kbn/core-saved-objects-utils-server", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/core/saved-objects/core-saved-objects-utils-server/src/index.ts b/packages/core/saved-objects/core-saved-objects-utils-server/src/index.ts new file mode 100644 index 0000000000000..3704cebf08398 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-utils-server/src/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { mergeSavedObjectMigrationMaps } from './merge_migration_maps'; +export { SavedObjectsErrorHelpers, type DecoratedError } from './saved_objects_error_helpers'; +export { + SavedObjectsUtils, + ALL_NAMESPACES_STRING, + DEFAULT_NAMESPACE_STRING, + FIND_DEFAULT_PAGE, + FIND_DEFAULT_PER_PAGE, +} from './saved_objects_utils'; diff --git a/src/core/server/saved_objects/migrations/utils.test.ts b/packages/core/saved-objects/core-saved-objects-utils-server/src/merge_migration_maps.test.ts similarity index 96% rename from src/core/server/saved_objects/migrations/utils.test.ts rename to packages/core/saved-objects/core-saved-objects-utils-server/src/merge_migration_maps.test.ts index e3378c3fb9e78..28b3ebd3735b4 100644 --- a/src/core/server/saved_objects/migrations/utils.test.ts +++ b/packages/core/saved-objects/core-saved-objects-utils-server/src/merge_migration_maps.test.ts @@ -11,7 +11,7 @@ import type { SavedObjectMigrationMap, SavedObjectUnsanitizedDoc, } from '@kbn/core-saved-objects-server'; -import { mergeSavedObjectMigrationMaps } from './utils'; +import { mergeSavedObjectMigrationMaps } from './merge_migration_maps'; describe('mergeSavedObjectMigrationMaps', () => { const obj1: SavedObjectMigrationMap = { diff --git a/src/core/server/saved_objects/migrations/utils.ts b/packages/core/saved-objects/core-saved-objects-utils-server/src/merge_migration_maps.ts similarity index 96% rename from src/core/server/saved_objects/migrations/utils.ts rename to packages/core/saved-objects/core-saved-objects-utils-server/src/merge_migration_maps.ts index 108317fc6698b..e5f2f1d74a7cf 100644 --- a/src/core/server/saved_objects/migrations/utils.ts +++ b/packages/core/saved-objects/core-saved-objects-utils-server/src/merge_migration_maps.ts @@ -7,12 +7,12 @@ */ import { mergeWith } from 'lodash'; -import { +import type { SavedObjectMigrationContext, SavedObjectMigrationFn, SavedObjectMigrationMap, SavedObjectUnsanitizedDoc, -} from '../..'; +} from '@kbn/core-saved-objects-server'; /** * Merges two saved object migration maps. diff --git a/src/core/server/saved_objects/service/lib/errors.test.ts b/packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.test.ts similarity index 99% rename from src/core/server/saved_objects/service/lib/errors.test.ts rename to packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.test.ts index 3bea693429254..6f312a09a56e9 100644 --- a/src/core/server/saved_objects/service/lib/errors.test.ts +++ b/packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.test.ts @@ -7,8 +7,7 @@ */ import Boom from '@hapi/boom'; - -import { SavedObjectsErrorHelpers } from './errors'; +import { SavedObjectsErrorHelpers } from './saved_objects_error_helpers'; describe('savedObjectsClient/errorTypes', () => { describe('BadRequest error', () => { diff --git a/src/core/server/saved_objects/service/lib/errors.ts b/packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts similarity index 100% rename from src/core/server/saved_objects/service/lib/errors.ts rename to packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_error_helpers.ts diff --git a/src/core/server/saved_objects/service/lib/utils.test.mock.ts b/packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.test.mock.ts similarity index 100% rename from src/core/server/saved_objects/service/lib/utils.test.mock.ts rename to packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.test.mock.ts diff --git a/src/core/server/saved_objects/service/lib/utils.test.ts b/packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.test.ts similarity index 94% rename from src/core/server/saved_objects/service/lib/utils.test.ts rename to packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.test.ts index 8c836afd087d3..717b52ef248ca 100644 --- a/src/core/server/saved_objects/service/lib/utils.test.ts +++ b/packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.test.ts @@ -6,10 +6,10 @@ * Side Public License, v 1. */ -import { mockUuidv1, mockUuidv5 } from './utils.test.mock'; +import { mockUuidv1, mockUuidv5 } from './saved_objects_utils.test.mock'; -import { SavedObjectsFindOptions } from '@kbn/core-saved-objects-api-server'; -import { SavedObjectsUtils } from './utils'; +import type { SavedObjectsFindOptions } from '@kbn/core-saved-objects-api-server'; +import { SavedObjectsUtils } from './saved_objects_utils'; describe('SavedObjectsUtils', () => { const { diff --git a/src/core/server/saved_objects/service/lib/utils.ts b/packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.ts similarity index 100% rename from src/core/server/saved_objects/service/lib/utils.ts rename to packages/core/saved-objects/core-saved-objects-utils-server/src/saved_objects_utils.ts diff --git a/packages/core/saved-objects/core-saved-objects-utils-server/tsconfig.json b/packages/core/saved-objects/core-saved-objects-utils-server/tsconfig.json new file mode 100644 index 0000000000000..39d3c7097814a --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-utils-server/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_kibana_client.ts b/packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_kibana_client.ts index bf068d7f33185..7bd2443031c80 100644 --- a/packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_kibana_client.ts +++ b/packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_kibana_client.ts @@ -54,9 +54,8 @@ export class ApmSynthtraceKibanaClient { }); } async fetchLatestApmPackageVersion(currentKibanaVersion: string) { - const url = - 'https://epr-snapshot.elastic.co/search?package=apm&prerelease=true&all=true&kibana.version='; - const response = await fetch(url + currentKibanaVersion, { method: 'GET' }); + const url = `https://epr-snapshot.elastic.co/search?package=apm&prerelease=true&all=true&kibana.version=${currentKibanaVersion}`; + const response = await fetch(url, { method: 'GET' }); const json = (await response.json()) as Array<{ version: string }>; const packageVersions = (json ?? []).map((item) => item.version).sort(Semver.rcompare); const validPackageVersions = packageVersions.filter((v) => Semver.valid(v)); @@ -71,7 +70,7 @@ export class ApmSynthtraceKibanaClient { async installApmPackage(kibanaUrl: string, version: string, username: string, password: string) { const packageVersion = await this.fetchLatestApmPackageVersion(version); - const response = await fetch(kibanaUrl + '/api/fleet/epm/packages/apm/' + packageVersion, { + const response = await fetch(`${kibanaUrl}/api/fleet/epm/packages/apm/${packageVersion}`, { method: 'POST', headers: { Authorization: 'Basic ' + Buffer.from(username + ':' + password).toString('base64'), diff --git a/packages/kbn-apm-synthtrace/src/lib/apm/index.ts b/packages/kbn-apm-synthtrace/src/lib/apm/index.ts index fcb8e078bf02a..a136daabee8f2 100644 --- a/packages/kbn-apm-synthtrace/src/lib/apm/index.ts +++ b/packages/kbn-apm-synthtrace/src/lib/apm/index.ts @@ -13,6 +13,7 @@ import { getChromeUserAgentDefaults } from './defaults/get_chrome_user_agent_def import { getBreakdownMetrics } from './processors/get_breakdown_metrics'; import { getApmWriteTargets } from './utils/get_apm_write_targets'; import { ApmSynthtraceEsClient } from './client/apm_synthtrace_es_client'; +import { ApmSynthtraceKibanaClient } from './client/apm_synthtrace_kibana_client'; import type { ApmException } from './apm_fields'; @@ -25,6 +26,7 @@ export const apm = { getBreakdownMetrics, getApmWriteTargets, ApmSynthtraceEsClient, + ApmSynthtraceKibanaClient, }; export type { ApmSynthtraceEsClient, ApmException }; diff --git a/packages/kbn-coloring/src/shared_components/coloring/assets/infinity.tsx b/packages/kbn-coloring/src/shared_components/coloring/assets/infinity.tsx new file mode 100644 index 0000000000000..8e28d98f579bd --- /dev/null +++ b/packages/kbn-coloring/src/shared_components/coloring/assets/infinity.tsx @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { EuiIconProps } from '@elastic/eui'; + +export const InfinityIcon = (props: Omit) => ( + + + + +); diff --git a/packages/kbn-coloring/src/shared_components/coloring/color_ranges/color_ranges.test.tsx b/packages/kbn-coloring/src/shared_components/coloring/color_ranges/color_ranges.test.tsx index 20a694a59283e..ce2f4f3e6a9fe 100644 --- a/packages/kbn-coloring/src/shared_components/coloring/color_ranges/color_ranges.test.tsx +++ b/packages/kbn-coloring/src/shared_components/coloring/color_ranges/color_ranges.test.tsx @@ -59,6 +59,7 @@ describe('Color Ranges', () => { continuity: 'none', }, showExtraActions: true, + displayInfinity: false, dispatch, }; }); diff --git a/packages/kbn-coloring/src/shared_components/coloring/color_ranges/color_ranges.tsx b/packages/kbn-coloring/src/shared_components/coloring/color_ranges/color_ranges.tsx index b8d8b3aa604d1..99055f904d2ea 100644 --- a/packages/kbn-coloring/src/shared_components/coloring/color_ranges/color_ranges.tsx +++ b/packages/kbn-coloring/src/shared_components/coloring/color_ranges/color_ranges.tsx @@ -26,6 +26,7 @@ export interface ColorRangesProps { colorRanges: ColorRange[]; paletteConfiguration: CustomPaletteParams | undefined; showExtraActions: boolean; + displayInfinity: boolean; dispatch: Dispatch; } @@ -33,6 +34,7 @@ export function ColorRanges({ colorRanges, paletteConfiguration, showExtraActions, + displayInfinity, dispatch, }: ColorRangesProps) { const [colorRangesValidity, setColorRangesValidity] = useState< @@ -65,6 +67,7 @@ export function ColorRanges({ index={index} validation={colorRangesValidity[index]} accessor="start" + displayInfinity={displayInfinity} /> ))} @@ -79,6 +82,7 @@ export function ColorRanges({ index={colorRanges.length - 1} validation={colorRangesValidity.last} accessor="end" + displayInfinity={displayInfinity} /> ) : null} diff --git a/packages/kbn-coloring/src/shared_components/coloring/color_ranges/color_ranges_item.tsx b/packages/kbn-coloring/src/shared_components/coloring/color_ranges/color_ranges_item.tsx index 99bb66b116647..33caaece5cfcf 100644 --- a/packages/kbn-coloring/src/shared_components/coloring/color_ranges/color_ranges_item.tsx +++ b/packages/kbn-coloring/src/shared_components/coloring/color_ranges/color_ranges_item.tsx @@ -52,6 +52,7 @@ export interface ColorRangesItemProps { continuity: PaletteContinuity; accessor: ColorRangeAccessor; validation?: ColorRangeValidation; + displayInfinity: boolean; } type ColorRangeItemMode = 'value' | 'auto' | 'edit'; @@ -67,10 +68,18 @@ const getMode = ( return (isLast ? checkIsMaxContinuity : checkIsMinContinuity)(continuity) ? 'auto' : 'edit'; }; -const getPlaceholderForAutoMode = (isLast: boolean) => +const getPlaceholderForAutoMode = (isLast: boolean, displayInfinity: boolean) => isLast - ? i18n.translate('coloring.dynamicColoring.customPalette.maxValuePlaceholder', { - defaultMessage: 'Max. value', + ? displayInfinity + ? i18n.translate('coloring.dynamicColoring.customPalette.extentPlaceholderInfinity', { + defaultMessage: 'Infinity', + }) + : i18n.translate('coloring.dynamicColoring.customPalette.maxValuePlaceholder', { + defaultMessage: 'Max. value', + }) + : displayInfinity + ? i18n.translate('coloring.dynamicColoring.customPalette.extentPlaceholderNegativeInfinity', { + defaultMessage: '-Infinity', }) : i18n.translate('coloring.dynamicColoring.customPalette.minValuePlaceholder', { defaultMessage: 'Min. value', @@ -102,6 +111,7 @@ export function ColorRangeItem({ validation, continuity, dispatch, + displayInfinity, }: ColorRangesItemProps) { const { dataBounds, palettes } = useContext(ColorRangesContext); const [popoverInFocus, setPopoverInFocus] = useState(false); @@ -220,7 +230,7 @@ export function ColorRangeItem({ } disabled={isDisabled} onChange={onValueChange} - placeholder={mode === 'auto' ? getPlaceholderForAutoMode(isLast) : ''} + placeholder={mode === 'auto' ? getPlaceholderForAutoMode(isLast, displayInfinity) : ''} append={getAppend(rangeType, mode)} onBlur={onLeaveFocus} data-test-subj={`lnsPalettePanel_dynamicColoring_range_value_${index}`} @@ -241,6 +251,7 @@ export function ColorRangeItem({ continuity={continuity} rangeType={rangeType} colorRanges={colorRanges} + displayInfinity={displayInfinity} dispatch={dispatch} accessor={accessor} /> diff --git a/packages/kbn-coloring/src/shared_components/coloring/color_ranges/color_ranges_item_buttons.tsx b/packages/kbn-coloring/src/shared_components/coloring/color_ranges/color_ranges_item_buttons.tsx index 41e1d56712b8c..e8f0d8269d0bd 100644 --- a/packages/kbn-coloring/src/shared_components/coloring/color_ranges/color_ranges_item_buttons.tsx +++ b/packages/kbn-coloring/src/shared_components/coloring/color_ranges/color_ranges_item_buttons.tsx @@ -20,6 +20,7 @@ import { TooltipWrapper } from '../tooltip_wrapper'; import type { ColorRangesActions, ColorRange, ColorRangeAccessor } from './types'; import { ColorRangesContext } from './color_ranges_context'; +import { InfinityIcon } from '../assets/infinity'; export interface ColorRangesItemButtonProps { index: number; @@ -28,6 +29,7 @@ export interface ColorRangesItemButtonProps { continuity: PaletteContinuity; dispatch: Dispatch; accessor: ColorRangeAccessor; + displayInfinity: boolean; } const switchContinuity = (isLast: boolean, continuity: PaletteContinuity) => { @@ -117,6 +119,7 @@ export function ColorRangeAutoDetectButton({ continuity, dispatch, accessor, + displayInfinity, }: ColorRangesItemButtonProps) { const { dataBounds, palettes } = useContext(ColorRangesContext); const isLast = isLastItem(accessor); @@ -131,8 +134,16 @@ export function ColorRangeAutoDetectButton({ }, [continuity, dataBounds, dispatch, isLast, palettes]); const tooltipContent = isLast - ? i18n.translate('coloring.dynamicColoring.customPalette.useAutoMaxValue', { - defaultMessage: `Use maximum data value`, + ? displayInfinity + ? i18n.translate('coloring.dynamicColoring.customPalette.useAutoMaxValueInfinity', { + defaultMessage: `Use positive infinity`, + }) + : i18n.translate('coloring.dynamicColoring.customPalette.useAutoMaxValue', { + defaultMessage: `Use maximum data value`, + }) + : displayInfinity + ? i18n.translate('coloring.dynamicColoring.customPalette.useAutoMinValueInfinity', { + defaultMessage: `Use negative infinity`, }) : i18n.translate('coloring.dynamicColoring.customPalette.useAutoMinValue', { defaultMessage: `Use minimum data value`, @@ -141,7 +152,7 @@ export function ColorRangeAutoDetectButton({ return ( { const idPrefix = useMemo(() => htmlIdGenerator()(), []); const colorRangesToShow = toColorRanges( @@ -190,6 +192,9 @@ export const CustomizablePalette = ({ showExtraActions={showExtraActions} paletteConfiguration={localState.activePalette?.params} colorRanges={localState.colorRanges} + displayInfinity={ + displayInfinity && localState.activePalette.params?.rangeType !== 'percent' + } dispatch={dispatch} /> diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 6c95efdba7330..d41a5356af082 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -125,6 +125,7 @@ pageLoadAssetSize: cloudSecurityPosture: 19109 visTypeGauge: 24113 unifiedSearch: 71059 + unifiedFieldList: 65500 data: 454087 eventAnnotation: 19334 screenshotting: 22870 diff --git a/packages/kbn-test/jest_integration_node/jest-preset.js b/packages/kbn-test/jest_integration_node/jest-preset.js index 8f5f433139ab7..ff50c2de40961 100644 --- a/packages/kbn-test/jest_integration_node/jest-preset.js +++ b/packages/kbn-test/jest_integration_node/jest-preset.js @@ -8,10 +8,14 @@ const preset = require('../jest-preset'); +const presetClone = { ...preset }; + +delete presetClone.testEnvironment; // simply redefining as `testEnvironment: 'node'` has some weird side-effects (https://github.com/elastic/kibana/pull/138877) + /** @typedef {import("@jest/types").Config.InitialOptions} JestConfig */ /** @type {JestConfig} */ module.exports = { - ...preset, + ...presetClone, testMatch: ['**/integration_tests**/*.test.{js,mjs,ts,tsx}'], testPathIgnorePatterns: preset.testPathIgnorePatterns.filter( (pattern) => !pattern.includes('integration_tests') @@ -40,7 +44,6 @@ module.exports = { ? [['json', { file: 'jest-integration.json' }]] : ['html', 'text'], - testEnvironment: 'node', snapshotSerializers: [], setupFiles: ['/node_modules/@kbn/test/target_node/jest/setup/babel_polyfill.js'], haste: { diff --git a/packages/kbn-test/jest_node/jest-preset.js b/packages/kbn-test/jest_node/jest-preset.js index 78d20414b9389..a28456e0da1f5 100644 --- a/packages/kbn-test/jest_node/jest-preset.js +++ b/packages/kbn-test/jest_node/jest-preset.js @@ -8,9 +8,12 @@ const preset = require('../jest-preset'); +const presetClone = { ...preset }; + +delete presetClone.testEnvironment; // simply redefining as `testEnvironment: 'node'` has some weird side-effects (https://github.com/elastic/kibana/pull/138877#issuecomment-1222366247) + module.exports = { - ...preset, - testEnvironment: 'node', + ...presetClone, snapshotSerializers: [], setupFiles: ['/node_modules/@kbn/test/target_node/jest/setup/babel_polyfill.js'], haste: { diff --git a/packages/kbn-test/src/jest/setup/enzyme.js b/packages/kbn-test/src/jest/setup/enzyme.js index c0ebcb93fc4a2..bf759523cfe7a 100644 --- a/packages/kbn-test/src/jest/setup/enzyme.js +++ b/packages/kbn-test/src/jest/setup/enzyme.js @@ -7,6 +7,6 @@ */ import { configure } from 'enzyme'; -import Adapter from 'enzyme-adapter-react-16'; +import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; configure({ adapter: new Adapter() }); diff --git a/renovate.json b/renovate.json index 49337a00d7246..7f60faf4a0a62 100644 --- a/renovate.json +++ b/renovate.json @@ -168,7 +168,7 @@ }, { "groupName": "react-query", - "packageNames": ["react-query"], + "packageNames": ["@tanstack/react-query", "@tanstack/react-query-devtools"], "reviewers": [ "team:response-ops", "team:kibana-cloud-security-posture", @@ -180,6 +180,17 @@ "matchBaseBranches": ["main"], "labels": ["release_note:skip", "backport:skip", "ci:all-cypress-suites"], "enabled": true + }, + { + "groupName": "react-hook-form", + "packageNames": ["react-hook-form"], + "reviewers": [ + "team:security-asset-management", + "team:uptime" + ], + "matchBaseBranches": ["main"], + "labels": ["release_note:skip", "backport:skip", "ci:all-cypress-suites"], + "enabled": true } ] } diff --git a/src/core/server/core_usage_data/core_usage_data_service.test.ts b/src/core/server/core_usage_data/core_usage_data_service.test.ts index c45b3fdb0a125..5ac2863745ee7 100644 --- a/src/core/server/core_usage_data/core_usage_data_service.test.ts +++ b/src/core/server/core_usage_data/core_usage_data_service.test.ts @@ -18,14 +18,14 @@ import { mockCoreContext } from '@kbn/core-base-server-mocks'; import { config as RawLoggingConfig } from '@kbn/core-logging-server-internal'; import { config as RawElasticsearchConfig } from '@kbn/core-elasticsearch-server-internal'; import { config as RawHttpConfig } from '@kbn/core-http-server-internal'; -import { savedObjectsConfig as RawSavedObjectsConfig } from '../saved_objects/saved_objects_config'; +import { savedObjectsConfig as RawSavedObjectsConfig } from '@kbn/core-saved-objects-base-server-internal'; import { httpServiceMock } from '@kbn/core-http-server-mocks'; import { metricsServiceMock } from '@kbn/core-metrics-server-mocks'; import { savedObjectsServiceMock } from '../saved_objects/saved_objects_service.mock'; import { CoreUsageDataService } from './core_usage_data_service'; import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks'; -import { typeRegistryMock } from '../saved_objects/saved_objects_type_registry.mock'; +import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import { CORE_USAGE_STATS_TYPE } from './constants'; import { CoreUsageStatsClient } from './core_usage_stats_client'; diff --git a/src/core/server/core_usage_data/core_usage_data_service.ts b/src/core/server/core_usage_data/core_usage_data_service.ts index f1e51a7e5f5aa..c5f2335e333e4 100644 --- a/src/core/server/core_usage_data/core_usage_data_service.ts +++ b/src/core/server/core_usage_data/core_usage_data_service.ts @@ -23,9 +23,12 @@ import type { HttpConfigType, InternalHttpServiceSetup } from '@kbn/core-http-se import type { ElasticsearchServiceStart } from '@kbn/core-elasticsearch-server'; import type { ElasticsearchConfigType } from '@kbn/core-elasticsearch-server-internal'; import type { MetricsServiceSetup, OpsMetrics } from '@kbn/core-metrics-server'; -import { SavedObjectsServiceStart, SavedObjectTypeRegistry } from '..'; +import { + LEGACY_URL_ALIAS_TYPE, + type SavedObjectsConfigType, +} from '@kbn/core-saved-objects-base-server-internal'; -import { SavedObjectsConfigType } from '../saved_objects/saved_objects_config'; +import { SavedObjectsServiceStart, SavedObjectTypeRegistry } from '..'; import type { CoreServicesUsageData, CoreUsageData, @@ -36,7 +39,6 @@ import type { } from './types'; import { isConfigured } from './is_configured'; import { coreUsageStatsType } from './core_usage_stats'; -import { LEGACY_URL_ALIAS_TYPE } from '../saved_objects/object_types'; import { CORE_USAGE_STATS_TYPE } from './constants'; import { CoreUsageStatsClient } from './core_usage_stats_client'; import { CoreIncrementUsageCounter } from './types'; diff --git a/src/core/server/core_usage_data/core_usage_stats_client.test.ts b/src/core/server/core_usage_data/core_usage_stats_client.test.ts index 6bcaa38bd0062..9a6984f6b5785 100644 --- a/src/core/server/core_usage_data/core_usage_stats_client.test.ts +++ b/src/core/server/core_usage_data/core_usage_stats_client.test.ts @@ -29,8 +29,8 @@ import { LEGACY_DASHBOARDS_EXPORT_STATS_PREFIX, BULK_RESOLVE_STATS_PREFIX, } from './core_usage_stats_client'; +import { DEFAULT_NAMESPACE_STRING } from '@kbn/core-saved-objects-utils-server'; import { CoreUsageStatsClient } from '.'; -import { DEFAULT_NAMESPACE_STRING } from '../saved_objects/service/lib/utils'; describe('CoreUsageStatsClient', () => { const setup = (namespace?: string) => { diff --git a/src/core/server/core_usage_data/core_usage_stats_client.ts b/src/core/server/core_usage_data/core_usage_stats_client.ts index 2dd8c77fd1876..c14776ff7ed81 100644 --- a/src/core/server/core_usage_data/core_usage_stats_client.ts +++ b/src/core/server/core_usage_data/core_usage_stats_client.ts @@ -6,9 +6,9 @@ * Side Public License, v 1. */ +import { DEFAULT_NAMESPACE_STRING } from '@kbn/core-saved-objects-utils-server'; import { CORE_USAGE_STATS_TYPE, CORE_USAGE_STATS_ID } from './constants'; import { CoreUsageStats } from './types'; -import { DEFAULT_NAMESPACE_STRING } from '../saved_objects/service/lib/utils'; import { ISavedObjectsRepository, SavedObjectsImportOptions, diff --git a/src/core/server/index.ts b/src/core/server/index.ts index 0f365366206d2..3327340c91a8c 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -388,6 +388,12 @@ export type { ISavedObjectsSerializer, SavedObjectsRequestHandlerContext, } from '@kbn/core-saved-objects-server'; +export { + SavedObjectsErrorHelpers, + SavedObjectsUtils, + mergeSavedObjectMigrationMaps, +} from '@kbn/core-saved-objects-utils-server'; +export { SavedObjectTypeRegistry } from '@kbn/core-saved-objects-base-server-internal'; export type { SavedObjectsRepository, @@ -396,14 +402,7 @@ export type { SavedObjectsImporter, SavedObjectsImportError, } from './saved_objects'; -export { - SavedObjectsClient, - SavedObjectsErrorHelpers, - SavedObjectsSerializer, - SavedObjectTypeRegistry, - SavedObjectsUtils, - mergeSavedObjectMigrationMaps, -} from './saved_objects'; +export { SavedObjectsClient } from './saved_objects'; export type { IUiSettingsClient, diff --git a/src/core/server/integration_tests/saved_objects/routes/delete_unknown_types.test.ts b/src/core/server/integration_tests/saved_objects/routes/delete_unknown_types.test.ts index 4a13921750d1f..b4a215169a376 100644 --- a/src/core/server/integration_tests/saved_objects/routes/delete_unknown_types.test.ts +++ b/src/core/server/integration_tests/saved_objects/routes/delete_unknown_types.test.ts @@ -8,8 +8,8 @@ import supertest from 'supertest'; import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks'; +import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import { registerDeleteUnknownTypesRoute } from '../../../saved_objects/routes/deprecations'; -import { typeRegistryMock } from '../../../saved_objects/saved_objects_type_registry.mock'; import { setupServer } from '../../../saved_objects/routes/test_utils'; import { SavedObjectsType } from '../../..'; import type { InternalSavedObjectsRequestHandlerContext } from '../../../saved_objects/internal_types'; diff --git a/src/core/server/integration_tests/saved_objects/routes/export.test.ts b/src/core/server/integration_tests/saved_objects/routes/export.test.ts index 358a23902d5e1..b107d1266fe3c 100644 --- a/src/core/server/integration_tests/saved_objects/routes/export.test.ts +++ b/src/core/server/integration_tests/saved_objects/routes/export.test.ts @@ -16,7 +16,7 @@ import { CoreUsageStatsClient } from '../../../core_usage_data'; import { coreUsageStatsClientMock } from '../../../core_usage_data/core_usage_stats_client.mock'; import { coreUsageDataServiceMock } from '../../../core_usage_data/core_usage_data_service.mock'; import { savedObjectsExporterMock } from '../../../saved_objects/export/saved_objects_exporter.mock'; -import { SavedObjectConfig } from '../../../saved_objects/saved_objects_config'; +import type { SavedObjectConfig } from '@kbn/core-saved-objects-base-server-internal'; import { registerExportRoute } from '../../../saved_objects/routes/export'; import { setupServer, createExportableType } from '../../../saved_objects/routes/test_utils'; import type { InternalSavedObjectsRequestHandlerContext } from '../../../saved_objects/internal_types'; diff --git a/src/core/server/integration_tests/saved_objects/routes/import.test.ts b/src/core/server/integration_tests/saved_objects/routes/import.test.ts index a9062b8d07919..ec6a483e8ae1f 100644 --- a/src/core/server/integration_tests/saved_objects/routes/import.test.ts +++ b/src/core/server/integration_tests/saved_objects/routes/import.test.ts @@ -9,14 +9,15 @@ jest.mock('uuid'); import supertest from 'supertest'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; import { registerImportRoute } from '../../../saved_objects/routes/import'; import { savedObjectsClientMock } from '../../../mocks'; import { CoreUsageStatsClient } from '../../../core_usage_data'; import { coreUsageStatsClientMock } from '../../../core_usage_data/core_usage_stats_client.mock'; import { coreUsageDataServiceMock } from '../../../core_usage_data/core_usage_data_service.mock'; -import { SavedObjectConfig } from '../../../saved_objects/saved_objects_config'; +import { SavedObjectConfig } from '@kbn/core-saved-objects-base-server-internal'; import { setupServer, createExportableType } from '../../../saved_objects/routes/test_utils'; -import { SavedObjectsErrorHelpers, SavedObjectsImporter } from '../../../saved_objects'; +import { SavedObjectsImporter } from '../../../saved_objects'; import type { InternalSavedObjectsRequestHandlerContext } from '../../../saved_objects/internal_types'; type SetupServerReturn = Awaited>; diff --git a/src/core/server/integration_tests/saved_objects/routes/resolve_import_errors.test.ts b/src/core/server/integration_tests/saved_objects/routes/resolve_import_errors.test.ts index defdbc5cfc1d6..5d26fde36fae4 100644 --- a/src/core/server/integration_tests/saved_objects/routes/resolve_import_errors.test.ts +++ b/src/core/server/integration_tests/saved_objects/routes/resolve_import_errors.test.ts @@ -15,7 +15,7 @@ import { CoreUsageStatsClient } from '../../../core_usage_data'; import { coreUsageStatsClientMock } from '../../../core_usage_data/core_usage_stats_client.mock'; import { coreUsageDataServiceMock } from '../../../core_usage_data/core_usage_data_service.mock'; import { setupServer, createExportableType } from '../../../saved_objects/routes/test_utils'; -import { SavedObjectConfig } from '../../../saved_objects/saved_objects_config'; +import { SavedObjectConfig } from '@kbn/core-saved-objects-base-server-internal'; import { SavedObjectsImporter } from '../../../saved_objects/import'; import type { InternalSavedObjectsRequestHandlerContext } from '../../../saved_objects/internal_types'; diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts index a98eaf50554f2..e2ae9a96b716d 100644 --- a/src/core/server/mocks.ts +++ b/src/core/server/mocks.ts @@ -23,6 +23,7 @@ import { httpServiceMock } from '@kbn/core-http-server-mocks'; import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks'; import { metricsServiceMock } from '@kbn/core-metrics-server-mocks'; import { capabilitiesServiceMock } from '@kbn/core-capabilities-server-mocks'; +import { typeRegistryMock as savedObjectsTypeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import type { PluginInitializerContext, CoreSetup, @@ -34,7 +35,6 @@ import type { import { httpResourcesMock } from './http_resources/http_resources_service.mock'; import { savedObjectsServiceMock } from './saved_objects/saved_objects_service.mock'; import { savedObjectsClientMock } from './saved_objects/service/saved_objects_client.mock'; -import { typeRegistryMock as savedObjectsTypeRegistryMock } from './saved_objects/saved_objects_type_registry.mock'; import { renderingMock } from './rendering/rendering_service.mock'; import { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock'; import { SharedGlobalConfig } from './plugins'; @@ -47,12 +47,12 @@ export { configServiceMock, configDeprecationsMock } from '@kbn/config-mocks'; export { loggingSystemMock } from '@kbn/core-logging-server-mocks'; export { httpServerMock, sessionStorageMock, httpServiceMock } from '@kbn/core-http-server-mocks'; export { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks'; +export { typeRegistryMock as savedObjectsTypeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; export { httpResourcesMock } from './http_resources/http_resources_service.mock'; export { savedObjectsRepositoryMock } from './saved_objects/service/lib/repository.mock'; export { savedObjectsServiceMock } from './saved_objects/saved_objects_service.mock'; export { savedObjectsClientMock } from './saved_objects/service/saved_objects_client.mock'; export { migrationMocks } from './saved_objects/migrations/mocks'; -export { typeRegistryMock as savedObjectsTypeRegistryMock } from './saved_objects/saved_objects_type_registry.mock'; export { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock'; export { metricsServiceMock } from '@kbn/core-metrics-server-mocks'; export { renderingMock } from './rendering/rendering_service.mock'; diff --git a/src/core/server/plugins/legacy_config.ts b/src/core/server/plugins/legacy_config.ts index 4bee00d615748..de86345c5b7ba 100644 --- a/src/core/server/plugins/legacy_config.ts +++ b/src/core/server/plugins/legacy_config.ts @@ -16,8 +16,11 @@ import { ElasticsearchConfigType, config as elasticsearchConfig, } from '@kbn/core-elasticsearch-server-internal'; +import { + type SavedObjectsConfigType, + savedObjectsConfig, +} from '@kbn/core-saved-objects-base-server-internal'; import { SharedGlobalConfig, SharedGlobalConfigKeys } from './types'; -import { SavedObjectsConfigType, savedObjectsConfig } from '../saved_objects/saved_objects_config'; const createGlobalConfig = ({ elasticsearch, diff --git a/src/core/server/plugins/types.ts b/src/core/server/plugins/types.ts index d41be740018a7..f8b20b66f5069 100644 --- a/src/core/server/plugins/types.ts +++ b/src/core/server/plugins/types.ts @@ -20,8 +20,7 @@ import type { import type { PluginName, PluginOpaqueId, PluginType } from '@kbn/core-base-common'; import type { NodeInfo } from '@kbn/core-node-server'; import type { ElasticsearchConfigType } from '@kbn/core-elasticsearch-server-internal'; - -import { SavedObjectsConfigType } from '../saved_objects/saved_objects_config'; +import type { SavedObjectsConfigType } from '@kbn/core-saved-objects-base-server-internal'; import { CorePreboot, CoreSetup, CoreStart } from '..'; type Maybe = T | undefined; diff --git a/src/core/server/saved_objects/deprecations/deprecation_factory.ts b/src/core/server/saved_objects/deprecations/deprecation_factory.ts index dea29cb7690ec..d330577151a52 100644 --- a/src/core/server/saved_objects/deprecations/deprecation_factory.ts +++ b/src/core/server/saved_objects/deprecations/deprecation_factory.ts @@ -7,8 +7,8 @@ */ import type { ISavedObjectTypeRegistry } from '@kbn/core-saved-objects-server'; +import type { SavedObjectConfig } from '@kbn/core-saved-objects-base-server-internal'; import type { RegisterDeprecationsConfig } from '../../deprecations'; -import type { SavedObjectConfig } from '../saved_objects_config'; import { getUnknownTypesDeprecations } from './unknown_object_types'; interface GetDeprecationProviderOptions { diff --git a/src/core/server/saved_objects/deprecations/unknown_object_types.test.ts b/src/core/server/saved_objects/deprecations/unknown_object_types.test.ts index 51f228f46438b..7e02f3343050d 100644 --- a/src/core/server/saved_objects/deprecations/unknown_object_types.test.ts +++ b/src/core/server/saved_objects/deprecations/unknown_object_types.test.ts @@ -9,7 +9,7 @@ import { getIndexForTypeMock } from './unknown_object_types.test.mocks'; import { deleteUnknownTypeObjects, getUnknownTypesDeprecations } from './unknown_object_types'; -import { typeRegistryMock } from '../saved_objects_type_registry.mock'; +import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; import { SavedObjectsType } from '../..'; import { createAggregateTypesSearchResponse } from '../migrations/actions/check_for_unknown_docs.mocks'; diff --git a/src/core/server/saved_objects/export/collect_exported_objects.test.ts b/src/core/server/saved_objects/export/collect_exported_objects.test.ts index 8aedffdd3745b..480a80e23cc97 100644 --- a/src/core/server/saved_objects/export/collect_exported_objects.test.ts +++ b/src/core/server/saved_objects/export/collect_exported_objects.test.ts @@ -15,7 +15,7 @@ import type { import { applyExportTransformsMock } from './collect_exported_objects.test.mocks'; import { savedObjectsClientMock } from '../../mocks'; import { loggerMock } from '@kbn/logging-mocks'; -import { SavedObjectTypeRegistry } from '../saved_objects_type_registry'; +import { SavedObjectTypeRegistry } from '@kbn/core-saved-objects-base-server-internal'; import { collectExportedObjects, ExclusionReason } from './collect_exported_objects'; const createObject = (parts: Partial): SavedObject => ({ diff --git a/src/core/server/saved_objects/export/saved_objects_exporter.test.ts b/src/core/server/saved_objects/export/saved_objects_exporter.test.ts index 8b4604087141c..55ee7642388ba 100644 --- a/src/core/server/saved_objects/export/saved_objects_exporter.test.ts +++ b/src/core/server/saved_objects/export/saved_objects_exporter.test.ts @@ -8,9 +8,9 @@ import { httpServerMock } from '@kbn/core-http-server-mocks'; import type { SavedObject } from '@kbn/core-saved-objects-common'; +import { SavedObjectTypeRegistry } from '@kbn/core-saved-objects-base-server-internal'; import { SavedObjectsExporter } from './saved_objects_exporter'; import { savedObjectsClientMock } from '../service/saved_objects_client.mock'; -import { SavedObjectTypeRegistry } from '../saved_objects_type_registry'; import { loggerMock, MockedLogger } from '@kbn/logging-mocks'; import { Readable } from 'stream'; import { createPromiseFromStreams, createConcatStream } from '@kbn/utils'; diff --git a/src/core/server/saved_objects/import/import_saved_objects.test.ts b/src/core/server/saved_objects/import/import_saved_objects.test.ts index 71990a8438ef3..9a086537c4761 100644 --- a/src/core/server/saved_objects/import/import_saved_objects.test.ts +++ b/src/core/server/saved_objects/import/import_saved_objects.test.ts @@ -30,8 +30,8 @@ import type { ISavedObjectTypeRegistry, SavedObjectsImportHook, } from '@kbn/core-saved-objects-server'; +import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import { savedObjectsClientMock } from '../../mocks'; -import { typeRegistryMock } from '../saved_objects_type_registry.mock'; import { importSavedObjectsFromStream, ImportSavedObjectsOptions } from './import_saved_objects'; import type { ImportStateMap } from './lib'; diff --git a/src/core/server/saved_objects/import/lib/check_conflicts.test.ts b/src/core/server/saved_objects/import/lib/check_conflicts.test.ts index 3dcbeacae1a2d..35be4e245086c 100644 --- a/src/core/server/saved_objects/import/lib/check_conflicts.test.ts +++ b/src/core/server/saved_objects/import/lib/check_conflicts.test.ts @@ -13,7 +13,7 @@ import type { SavedObjectsImportRetry, } from '@kbn/core-saved-objects-common'; import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; -import { SavedObjectsErrorHelpers } from '../../service'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; import { checkConflicts } from './check_conflicts'; jest.mock('uuid', () => ({ diff --git a/src/core/server/saved_objects/import/lib/check_origin_conflicts.test.ts b/src/core/server/saved_objects/import/lib/check_origin_conflicts.test.ts index ed80c7cdf0751..756214e2ddc03 100644 --- a/src/core/server/saved_objects/import/lib/check_origin_conflicts.test.ts +++ b/src/core/server/saved_objects/import/lib/check_origin_conflicts.test.ts @@ -16,9 +16,9 @@ import type { } from '@kbn/core-saved-objects-common'; import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; import type { ISavedObjectTypeRegistry } from '@kbn/core-saved-objects-server'; +import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import { checkOriginConflicts } from './check_origin_conflicts'; import { savedObjectsClientMock } from '../../../mocks'; -import { typeRegistryMock } from '../../saved_objects_type_registry.mock'; import type { ImportStateMap } from './types'; jest.mock('uuid', () => ({ diff --git a/src/core/server/saved_objects/import/lib/check_reference_origins.test.ts b/src/core/server/saved_objects/import/lib/check_reference_origins.test.ts index cff3a4d5540a0..fce677ec6ea46 100644 --- a/src/core/server/saved_objects/import/lib/check_reference_origins.test.ts +++ b/src/core/server/saved_objects/import/lib/check_reference_origins.test.ts @@ -15,7 +15,7 @@ import type { import type { ISavedObjectTypeRegistry } from '@kbn/core-saved-objects-server'; import { checkReferenceOrigins, CheckReferenceOriginsParams } from './check_reference_origins'; import { savedObjectsClientMock } from '../../../mocks'; -import { typeRegistryMock } from '../../saved_objects_type_registry.mock'; +import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import type { ImportStateMap } from './types'; const MULTI_NS_TYPE = 'multi'; diff --git a/src/core/server/saved_objects/import/lib/create_saved_objects.test.ts b/src/core/server/saved_objects/import/lib/create_saved_objects.test.ts index a29765849c364..363d2bee5899e 100644 --- a/src/core/server/saved_objects/import/lib/create_saved_objects.test.ts +++ b/src/core/server/saved_objects/import/lib/create_saved_objects.test.ts @@ -9,8 +9,8 @@ import { savedObjectsClientMock } from '../../../mocks'; import type { SavedObject, SavedObjectsImportFailure } from '@kbn/core-saved-objects-common'; import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; import { createSavedObjects } from './create_saved_objects'; -import { SavedObjectsErrorHelpers } from '../../service'; import { extractErrors } from './extract_errors'; type CreateSavedObjectsParams = Parameters[0]; diff --git a/src/core/server/saved_objects/import/lib/extract_errors.test.ts b/src/core/server/saved_objects/import/lib/extract_errors.test.ts index 20a8d40b62a70..c4d3d0e4c2722 100644 --- a/src/core/server/saved_objects/import/lib/extract_errors.test.ts +++ b/src/core/server/saved_objects/import/lib/extract_errors.test.ts @@ -8,8 +8,8 @@ import type { SavedObject } from '@kbn/core-saved-objects-common'; import type { CreatedObject } from '@kbn/core-saved-objects-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; import { extractErrors } from './extract_errors'; -import { SavedObjectsErrorHelpers } from '../../service'; describe('extractErrors()', () => { test('returns empty array when no errors exist', () => { diff --git a/src/core/server/saved_objects/import/lib/validate_references.test.ts b/src/core/server/saved_objects/import/lib/validate_references.test.ts index 2e6f1a5e0a9a2..2a8095ef78ce8 100644 --- a/src/core/server/saved_objects/import/lib/validate_references.test.ts +++ b/src/core/server/saved_objects/import/lib/validate_references.test.ts @@ -6,10 +6,10 @@ * Side Public License, v 1. */ +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; import type { ValidateReferencesParams } from './validate_references'; import { validateReferences } from './validate_references'; import { savedObjectsClientMock } from '../../../mocks'; -import { SavedObjectsErrorHelpers } from '../../service'; function setup({ objects = [], diff --git a/src/core/server/saved_objects/import/resolve_import_errors.test.ts b/src/core/server/saved_objects/import/resolve_import_errors.test.ts index bc297182b281d..288e58aabb4c2 100644 --- a/src/core/server/saved_objects/import/resolve_import_errors.test.ts +++ b/src/core/server/saved_objects/import/resolve_import_errors.test.ts @@ -36,8 +36,8 @@ import type { ISavedObjectTypeRegistry, SavedObjectsImportHook, } from '@kbn/core-saved-objects-server'; +import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import { savedObjectsClientMock } from '../../mocks'; -import { typeRegistryMock } from '../saved_objects_type_registry.mock'; import { resolveSavedObjectsImportErrors, ResolveSavedObjectsImportErrorsOptions, diff --git a/src/core/server/saved_objects/index.ts b/src/core/server/saved_objects/index.ts index 4a7a14b45b665..4ccc4f1bcdb30 100644 --- a/src/core/server/saved_objects/index.ts +++ b/src/core/server/saved_objects/index.ts @@ -14,8 +14,6 @@ export type { SavedObjectsImportError } from './import'; export type { SavedObjectsExporter, SavedObjectsExportError } from './export'; -export { SavedObjectsSerializer } from './serialization'; - export { SavedObjectsService } from './saved_objects_service'; export type { @@ -23,10 +21,4 @@ export type { InternalSavedObjectsServiceSetup, } from './saved_objects_service'; -export type { SavedObjectsTypeMappingDefinitions } from './mappings'; - -export { mergeSavedObjectMigrationMaps } from './migrations'; - -export { savedObjectsConfig, savedObjectsMigrationConfig } from './saved_objects_config'; -export { SavedObjectTypeRegistry } from './saved_objects_type_registry'; export { CoreSavedObjectsRouteHandlerContext } from './saved_objects_route_handler_context'; diff --git a/src/core/server/saved_objects/migrations/actions/create_index.ts b/src/core/server/saved_objects/migrations/actions/create_index.ts index 0f6b062a42163..f48918d679624 100644 --- a/src/core/server/saved_objects/migrations/actions/create_index.ts +++ b/src/core/server/saved_objects/migrations/actions/create_index.ts @@ -11,8 +11,8 @@ import * as TaskEither from 'fp-ts/lib/TaskEither'; import { pipe } from 'fp-ts/lib/pipeable'; import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; +import type { IndexMapping } from '@kbn/core-saved-objects-base-server-internal'; import { AcknowledgeResponse } from '.'; -import { IndexMapping } from '../../mappings'; import { catchRetryableEsClientErrors, RetryableEsClientError, diff --git a/src/core/server/saved_objects/migrations/actions/fetch_indices.ts b/src/core/server/saved_objects/migrations/actions/fetch_indices.ts index 922797f2ba268..0eb43380a6990 100644 --- a/src/core/server/saved_objects/migrations/actions/fetch_indices.ts +++ b/src/core/server/saved_objects/migrations/actions/fetch_indices.ts @@ -5,10 +5,11 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import * as TaskEither from 'fp-ts/lib/TaskEither'; import * as Either from 'fp-ts/lib/Either'; import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; -import { IndexMapping } from '../../mappings'; +import type { IndexMapping } from '@kbn/core-saved-objects-base-server-internal'; import { catchRetryableEsClientErrors, RetryableEsClientError, diff --git a/src/core/server/saved_objects/migrations/actions/update_and_pickup_mappings.ts b/src/core/server/saved_objects/migrations/actions/update_and_pickup_mappings.ts index bd1c2d6ff7614..a5fe33e9bbcf5 100644 --- a/src/core/server/saved_objects/migrations/actions/update_and_pickup_mappings.ts +++ b/src/core/server/saved_objects/migrations/actions/update_and_pickup_mappings.ts @@ -10,7 +10,7 @@ import * as Either from 'fp-ts/lib/Either'; import * as TaskEither from 'fp-ts/lib/TaskEither'; import { pipe } from 'fp-ts/lib/pipeable'; import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; -import { IndexMapping } from '../../mappings'; +import type { IndexMapping } from '@kbn/core-saved-objects-base-server-internal'; import { catchRetryableEsClientErrors, RetryableEsClientError, diff --git a/src/core/server/saved_objects/migrations/core/build_active_mappings.test.ts b/src/core/server/saved_objects/migrations/core/build_active_mappings.test.ts index 7a57a7a8a08d3..7f1542ffc6008 100644 --- a/src/core/server/saved_objects/migrations/core/build_active_mappings.test.ts +++ b/src/core/server/saved_objects/migrations/core/build_active_mappings.test.ts @@ -6,7 +6,10 @@ * Side Public License, v 1. */ -import { IndexMapping, SavedObjectsTypeMappingDefinitions } from '../../mappings'; +import type { + IndexMapping, + SavedObjectsTypeMappingDefinitions, +} from '@kbn/core-saved-objects-base-server-internal'; import { buildActiveMappings, diffMappings } from './build_active_mappings'; describe('buildActiveMappings', () => { diff --git a/src/core/server/saved_objects/migrations/core/build_active_mappings.ts b/src/core/server/saved_objects/migrations/core/build_active_mappings.ts index 62f5bd1454419..1a0a502f655bd 100644 --- a/src/core/server/saved_objects/migrations/core/build_active_mappings.ts +++ b/src/core/server/saved_objects/migrations/core/build_active_mappings.ts @@ -13,7 +13,10 @@ import crypto from 'crypto'; import { cloneDeep, mapValues } from 'lodash'; import type { SavedObjectsMappingProperties } from '@kbn/core-saved-objects-server'; -import { IndexMapping, SavedObjectsTypeMappingDefinitions } from '../../mappings'; +import type { + IndexMapping, + SavedObjectsTypeMappingDefinitions, +} from '@kbn/core-saved-objects-base-server-internal'; /** * Creates an index mapping with the core properties required by saved object diff --git a/src/core/server/saved_objects/migrations/core/build_index_map.test.ts b/src/core/server/saved_objects/migrations/core/build_index_map.test.ts index 5f5d587eec359..ec82b0ac2c92f 100644 --- a/src/core/server/saved_objects/migrations/core/build_index_map.test.ts +++ b/src/core/server/saved_objects/migrations/core/build_index_map.test.ts @@ -7,8 +7,8 @@ */ import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; +import { SavedObjectTypeRegistry } from '@kbn/core-saved-objects-base-server-internal'; import { createIndexMap } from './build_index_map'; -import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry'; const createRegistry = (...types: Array>) => { const registry = new SavedObjectTypeRegistry(); diff --git a/src/core/server/saved_objects/migrations/core/build_index_map.ts b/src/core/server/saved_objects/migrations/core/build_index_map.ts index 1e1adcf0dcbc8..225b3bb422925 100644 --- a/src/core/server/saved_objects/migrations/core/build_index_map.ts +++ b/src/core/server/saved_objects/migrations/core/build_index_map.ts @@ -7,7 +7,7 @@ */ import type { ISavedObjectTypeRegistry } from '@kbn/core-saved-objects-server'; -import type { SavedObjectsTypeMappingDefinitions } from '../../mappings'; +import type { SavedObjectsTypeMappingDefinitions } from '@kbn/core-saved-objects-base-server-internal'; export interface CreateIndexMapOptions { kibanaIndexName: string; diff --git a/src/core/server/saved_objects/migrations/core/disable_unknown_type_mapping_fields.ts b/src/core/server/saved_objects/migrations/core/disable_unknown_type_mapping_fields.ts index b1e25e6e024fb..8b1eccbe09d78 100644 --- a/src/core/server/saved_objects/migrations/core/disable_unknown_type_mapping_fields.ts +++ b/src/core/server/saved_objects/migrations/core/disable_unknown_type_mapping_fields.ts @@ -7,7 +7,7 @@ */ import type { SavedObjectsMappingProperties } from '@kbn/core-saved-objects-server'; -import { IndexMapping } from '../../mappings'; +import type { IndexMapping } from '@kbn/core-saved-objects-base-server-internal'; /** * Merges the active mappings and the source mappings while disabling the diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.test.mock.ts b/src/core/server/saved_objects/migrations/core/document_migrator.test.mock.ts index ee0b18af5ac0d..df7914a55876f 100644 --- a/src/core/server/saved_objects/migrations/core/document_migrator.test.mock.ts +++ b/src/core/server/saved_objects/migrations/core/document_migrator.test.mock.ts @@ -7,8 +7,9 @@ */ const mockGetConvertedObjectId = jest.fn().mockReturnValue('uuidv5'); -jest.mock('../../service/lib/utils', () => { - const actual = jest.requireActual('../../service/lib/utils'); + +jest.mock('@kbn/core-saved-objects-utils-server', () => { + const actual = jest.requireActual('@kbn/core-saved-objects-utils-server'); return { ...actual, SavedObjectsUtils: { diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.test.ts b/src/core/server/saved_objects/migrations/core/document_migrator.test.ts index 1d12d2062be93..a0dd1cfddc3a4 100644 --- a/src/core/server/saved_objects/migrations/core/document_migrator.test.ts +++ b/src/core/server/saved_objects/migrations/core/document_migrator.test.ts @@ -10,11 +10,13 @@ import { mockGetConvertedObjectId } from './document_migrator.test.mock'; import { set } from '@kbn/safer-lodash-set'; import _ from 'lodash'; import type { SavedObjectUnsanitizedDoc, SavedObjectsType } from '@kbn/core-saved-objects-server'; +import { + SavedObjectTypeRegistry, + LEGACY_URL_ALIAS_TYPE, +} from '@kbn/core-saved-objects-base-server-internal'; import { DocumentMigrator } from './document_migrator'; import { TransformSavedObjectDocumentError } from './transform_saved_object_document_error'; import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; -import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry'; -import { LEGACY_URL_ALIAS_TYPE } from '../../object_types'; const mockLoggerFactory = loggingSystemMock.create(); const mockLogger = mockLoggerFactory.get('mock logger'); diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.ts b/src/core/server/saved_objects/migrations/core/document_migrator.ts index 677b2a685f64e..1040699a99e9d 100644 --- a/src/core/server/saved_objects/migrations/core/document_migrator.ts +++ b/src/core/server/saved_objects/migrations/core/document_migrator.ts @@ -58,10 +58,13 @@ import type { SavedObjectMigrationFn, SavedObjectMigrationMap, } from '@kbn/core-saved-objects-server'; +import { DEFAULT_NAMESPACE_STRING, SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server'; +import { + type LegacyUrlAlias, + LEGACY_URL_ALIAS_TYPE, +} from '@kbn/core-saved-objects-base-server-internal'; import { MigrationLogger } from './migration_logger'; import { TransformSavedObjectDocumentError } from '.'; -import { DEFAULT_NAMESPACE_STRING, SavedObjectsUtils } from '../../service/lib/utils'; -import { LegacyUrlAlias, LEGACY_URL_ALIAS_TYPE } from '../../object_types'; const DEFAULT_MINIMUM_CONVERT_VERSION = '8.0.0'; diff --git a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts index 09ec13bcae05b..c9d7b78953e28 100644 --- a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts +++ b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts @@ -9,8 +9,10 @@ import { set } from '@kbn/safer-lodash-set'; import * as Either from 'fp-ts/lib/Either'; import _ from 'lodash'; -import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry'; -import { SavedObjectsSerializer } from '../../serialization'; +import { + SavedObjectTypeRegistry, + SavedObjectsSerializer, +} from '@kbn/core-saved-objects-base-server-internal'; import { DocumentsTransformFailed, DocumentsTransformSuccess, diff --git a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts index 8534d7e497be0..d1ca65f1dd103 100644 --- a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts +++ b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts @@ -16,7 +16,7 @@ import type { SavedObjectsRawDoc, SavedObjectUnsanitizedDoc, } from '@kbn/core-saved-objects-server'; -import { SavedObjectsSerializer } from '../../serialization'; +import { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal'; import { MigrateAndConvertFn } from './document_migrator'; import { TransformSavedObjectDocumentError } from '.'; diff --git a/src/core/server/saved_objects/migrations/index.ts b/src/core/server/saved_objects/migrations/index.ts index 54416f423a79a..707b777330ec3 100644 --- a/src/core/server/saved_objects/migrations/index.ts +++ b/src/core/server/saved_objects/migrations/index.ts @@ -9,4 +9,3 @@ export type { MigrationResult } from './core'; export { KibanaMigrator } from './kibana_migrator'; export type { IKibanaMigrator, KibanaMigratorStatus } from './kibana_migrator'; -export { mergeSavedObjectMigrationMaps } from './utils'; diff --git a/src/core/server/saved_objects/migrations/initial_state.test.ts b/src/core/server/saved_objects/migrations/initial_state.test.ts index 32e3b9f50f4a3..6d49b17d4fa80 100644 --- a/src/core/server/saved_objects/migrations/initial_state.test.ts +++ b/src/core/server/saved_objects/migrations/initial_state.test.ts @@ -10,9 +10,11 @@ import { ByteSizeValue } from '@kbn/config-schema'; import * as Option from 'fp-ts/Option'; import type { DocLinksServiceSetup } from '@kbn/core-doc-links-server'; import { docLinksServiceMock } from '@kbn/core-doc-links-server-mocks'; +import { + type SavedObjectsMigrationConfigType, + SavedObjectTypeRegistry, +} from '@kbn/core-saved-objects-base-server-internal'; import { loggingSystemMock } from '../../mocks'; -import { SavedObjectsMigrationConfigType } from '../saved_objects_config'; -import { SavedObjectTypeRegistry } from '../saved_objects_type_registry'; import { createInitialState } from './initial_state'; const mockLogger = loggingSystemMock.create(); diff --git a/src/core/server/saved_objects/migrations/initial_state.ts b/src/core/server/saved_objects/migrations/initial_state.ts index 861ec75e2d875..1843227934bcd 100644 --- a/src/core/server/saved_objects/migrations/initial_state.ts +++ b/src/core/server/saved_objects/migrations/initial_state.ts @@ -11,8 +11,10 @@ import type { DocLinksServiceStart } from '@kbn/core-doc-links-server'; import type { Logger } from '@kbn/logging'; import type { SavedObjectsMigrationVersion } from '@kbn/core-saved-objects-common'; import type { ISavedObjectTypeRegistry } from '@kbn/core-saved-objects-server'; -import type { IndexMapping } from '../mappings'; -import type { SavedObjectsMigrationConfigType } from '../saved_objects_config'; +import type { + IndexMapping, + SavedObjectsMigrationConfigType, +} from '@kbn/core-saved-objects-base-server-internal'; import type { InitState } from './state'; import { excludeUnusedTypesQuery } from './core'; diff --git a/src/core/server/saved_objects/migrations/kibana_migrator.test.ts b/src/core/server/saved_objects/migrations/kibana_migrator.test.ts index 6806e5a9c80a1..5d1cb32eca6d2 100644 --- a/src/core/server/saved_objects/migrations/kibana_migrator.test.ts +++ b/src/core/server/saved_objects/migrations/kibana_migrator.test.ts @@ -12,8 +12,8 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; import type { SavedObjectsType } from '@kbn/core-saved-objects-server'; +import { SavedObjectTypeRegistry } from '@kbn/core-saved-objects-base-server-internal'; import { KibanaMigratorOptions, KibanaMigrator } from './kibana_migrator'; -import { SavedObjectTypeRegistry } from '../saved_objects_type_registry'; import { DocumentMigrator } from './core/document_migrator'; import { ByteSizeValue } from '@kbn/config-schema'; import { docLinksServiceMock } from '@kbn/core-doc-links-server-mocks'; diff --git a/src/core/server/saved_objects/migrations/kibana_migrator.ts b/src/core/server/saved_objects/migrations/kibana_migrator.ts index 146dd600ab4d0..2a8f9a9c3ee0f 100644 --- a/src/core/server/saved_objects/migrations/kibana_migrator.ts +++ b/src/core/server/saved_objects/migrations/kibana_migrator.ts @@ -22,12 +22,15 @@ import type { SavedObjectsRawDoc, ISavedObjectTypeRegistry, } from '@kbn/core-saved-objects-server'; -import { IndexMapping, SavedObjectsTypeMappingDefinitions } from '../mappings'; -import { SavedObjectsSerializer } from '../serialization'; +import { + SavedObjectsSerializer, + type IndexMapping, + type SavedObjectsTypeMappingDefinitions, + type SavedObjectsMigrationConfigType, +} from '@kbn/core-saved-objects-base-server-internal'; import { buildActiveMappings, MigrationResult, MigrationStatus } from './core'; import { DocumentMigrator, VersionedTransformer } from './core/document_migrator'; import { createIndexMap } from './core/build_index_map'; -import { SavedObjectsMigrationConfigType } from '../saved_objects_config'; import { runResilientMigrator } from './run_resilient_migrator'; import { migrateRawDocsSafely } from './core/migrate_raw_docs'; diff --git a/src/core/server/saved_objects/migrations/migrations_state_action_machine.test.ts b/src/core/server/saved_objects/migrations/migrations_state_action_machine.test.ts index db319c63e216f..1c4644fb43325 100644 --- a/src/core/server/saved_objects/migrations/migrations_state_action_machine.test.ts +++ b/src/core/server/saved_objects/migrations/migrations_state_action_machine.test.ts @@ -11,8 +11,8 @@ import { migrationStateActionMachine } from './migrations_state_action_machine'; import { docLinksServiceMock } from '@kbn/core-doc-links-server-mocks'; import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; import { LoggerAdapter } from '@kbn/core-logging-server-internal'; +import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import { elasticsearchServiceMock } from '../../mocks'; -import { typeRegistryMock } from '../saved_objects_type_registry.mock'; import * as Either from 'fp-ts/lib/Either'; import * as Option from 'fp-ts/lib/Option'; import { errors } from '@elastic/elasticsearch'; diff --git a/src/core/server/saved_objects/migrations/model/helpers.ts b/src/core/server/saved_objects/migrations/model/helpers.ts index ea28c14f9a5f0..5f84dc01af008 100644 --- a/src/core/server/saved_objects/migrations/model/helpers.ts +++ b/src/core/server/saved_objects/migrations/model/helpers.ts @@ -12,8 +12,8 @@ import type { QueryDslQueryContainer, } from '@elastic/elasticsearch/lib/api/types'; import * as Either from 'fp-ts/lib/Either'; +import type { IndexMapping } from '@kbn/core-saved-objects-base-server-internal'; import type { State } from '../state'; -import type { IndexMapping } from '../../mappings'; import type { FetchIndexResponse } from '../actions'; /** diff --git a/src/core/server/saved_objects/migrations/run_resilient_migrator.ts b/src/core/server/saved_objects/migrations/run_resilient_migrator.ts index 9ca3d3dbaaa60..6a6153b997146 100644 --- a/src/core/server/saved_objects/migrations/run_resilient_migrator.ts +++ b/src/core/server/saved_objects/migrations/run_resilient_migrator.ts @@ -11,14 +11,16 @@ import type { DocLinksServiceStart } from '@kbn/core-doc-links-server'; import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import type { SavedObjectsMigrationVersion } from '@kbn/core-saved-objects-common'; import type { ISavedObjectTypeRegistry } from '@kbn/core-saved-objects-server'; -import { IndexMapping } from '../mappings'; +import type { + IndexMapping, + SavedObjectsMigrationConfigType, +} from '@kbn/core-saved-objects-base-server-internal'; import type { TransformRawDocs } from './types'; import { MigrationResult } from './core'; import { next } from './next'; import { model } from './model'; import { createInitialState } from './initial_state'; import { migrationStateActionMachine } from './migrations_state_action_machine'; -import { SavedObjectsMigrationConfigType } from '../saved_objects_config'; /** * To avoid the Elasticsearch-js client aborting our requests before we diff --git a/src/core/server/saved_objects/migrations/state.ts b/src/core/server/saved_objects/migrations/state.ts index d94d49ecec7aa..eb8d2e2fbb05b 100644 --- a/src/core/server/saved_objects/migrations/state.ts +++ b/src/core/server/saved_objects/migrations/state.ts @@ -13,9 +13,9 @@ import type { SavedObjectsRawDoc, SavedObjectTypeExcludeFromUpgradeFilterHook, } from '@kbn/core-saved-objects-server'; +import type { IndexMapping } from '@kbn/core-saved-objects-base-server-internal'; import type { ControlState } from './state_action_machine'; import type { AliasAction } from './actions'; -import type { IndexMapping } from '../mappings'; import type { TransformErrorObjects } from './core'; import type { MigrationLog, Progress } from './types'; diff --git a/src/core/server/saved_objects/object_types/index.ts b/src/core/server/saved_objects/object_types/index.ts index 11a2c1973b09a..55c0db7ffcdbc 100644 --- a/src/core/server/saved_objects/object_types/index.ts +++ b/src/core/server/saved_objects/object_types/index.ts @@ -6,6 +6,4 @@ * Side Public License, v 1. */ -export { LEGACY_URL_ALIAS_TYPE } from './constants'; -export type { LegacyUrlAlias } from './types'; export { registerCoreObjectTypes } from './registration'; diff --git a/src/core/server/saved_objects/object_types/registration.test.ts b/src/core/server/saved_objects/object_types/registration.test.ts index 724b9baababef..2c8a467e32842 100644 --- a/src/core/server/saved_objects/object_types/registration.test.ts +++ b/src/core/server/saved_objects/object_types/registration.test.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { typeRegistryMock } from '../saved_objects_type_registry.mock'; -import { LEGACY_URL_ALIAS_TYPE } from './constants'; +import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; +import { LEGACY_URL_ALIAS_TYPE } from '@kbn/core-saved-objects-base-server-internal'; import { registerCoreObjectTypes } from './registration'; describe('Core saved object types registration', () => { diff --git a/src/core/server/saved_objects/object_types/registration.ts b/src/core/server/saved_objects/object_types/registration.ts index d93b1cc2e2a23..5d31cb1e03077 100644 --- a/src/core/server/saved_objects/object_types/registration.ts +++ b/src/core/server/saved_objects/object_types/registration.ts @@ -7,9 +7,11 @@ */ import type { ISavedObjectTypeRegistry, SavedObjectsType } from '@kbn/core-saved-objects-server'; -import { LEGACY_URL_ALIAS_TYPE } from './constants'; -import { SavedObjectTypeRegistry } from '..'; -import type { LegacyUrlAlias } from './types'; +import { + SavedObjectTypeRegistry, + LEGACY_URL_ALIAS_TYPE, + type LegacyUrlAlias, +} from '@kbn/core-saved-objects-base-server-internal'; const legacyUrlAliasType: SavedObjectsType = { name: LEGACY_URL_ALIAS_TYPE, diff --git a/src/core/server/saved_objects/routes/export.ts b/src/core/server/saved_objects/routes/export.ts index bd89a2845626a..0ab75a3c7101d 100644 --- a/src/core/server/saved_objects/routes/export.ts +++ b/src/core/server/saved_objects/routes/export.ts @@ -15,8 +15,8 @@ import type { SavedObjectsExportByTypeOptions, SavedObjectsExportByObjectOptions, } from '@kbn/core-saved-objects-server'; +import type { SavedObjectConfig } from '@kbn/core-saved-objects-base-server-internal'; import { InternalCoreUsageDataSetup } from '../../core_usage_data'; -import { SavedObjectConfig } from '../saved_objects_config'; import { SavedObjectsExportError } from '../export'; import type { InternalSavedObjectRouter } from '../internal_types'; import { validateTypes, validateObjects, catchAndReturnBoomErrors } from './utils'; diff --git a/src/core/server/saved_objects/routes/import.ts b/src/core/server/saved_objects/routes/import.ts index 0c56acf9e2d68..717cc50a13e60 100644 --- a/src/core/server/saved_objects/routes/import.ts +++ b/src/core/server/saved_objects/routes/import.ts @@ -9,8 +9,8 @@ import { Readable } from 'stream'; import { extname } from 'path'; import { schema } from '@kbn/config-schema'; +import type { SavedObjectConfig } from '@kbn/core-saved-objects-base-server-internal'; import { InternalCoreUsageDataSetup } from '../../core_usage_data'; -import { SavedObjectConfig } from '../saved_objects_config'; import { SavedObjectsImportError } from '../import'; import type { InternalSavedObjectRouter } from '../internal_types'; import { catchAndReturnBoomErrors, createSavedObjectsStreamFromNdJson } from './utils'; diff --git a/src/core/server/saved_objects/routes/index.ts b/src/core/server/saved_objects/routes/index.ts index edc52efdeaef8..f49c604a37c0c 100644 --- a/src/core/server/saved_objects/routes/index.ts +++ b/src/core/server/saved_objects/routes/index.ts @@ -8,8 +8,8 @@ import type { Logger } from '@kbn/logging'; import type { InternalHttpServiceSetup } from '@kbn/core-http-server-internal'; +import type { SavedObjectConfig } from '@kbn/core-saved-objects-base-server-internal'; import { InternalCoreUsageDataSetup } from '../../core_usage_data'; -import { SavedObjectConfig } from '../saved_objects_config'; import { IKibanaMigrator } from '../migrations'; import type { InternalSavedObjectsRequestHandlerContext } from '../internal_types'; import { registerGetRoute } from './get'; diff --git a/src/core/server/saved_objects/routes/resolve_import_errors.ts b/src/core/server/saved_objects/routes/resolve_import_errors.ts index 4bedec1715a4f..ed919693e1b86 100644 --- a/src/core/server/saved_objects/routes/resolve_import_errors.ts +++ b/src/core/server/saved_objects/routes/resolve_import_errors.ts @@ -8,10 +8,10 @@ import { extname } from 'path'; import { Readable } from 'stream'; -import { schema } from '@kbn/config-schema'; import { chain } from 'lodash'; +import { schema } from '@kbn/config-schema'; +import type { SavedObjectConfig } from '@kbn/core-saved-objects-base-server-internal'; import { InternalCoreUsageDataSetup } from '../../core_usage_data'; -import { SavedObjectConfig } from '../saved_objects_config'; import { SavedObjectsImportError } from '../import'; import type { InternalSavedObjectRouter } from '../internal_types'; import { catchAndReturnBoomErrors, createSavedObjectsStreamFromNdJson } from './utils'; diff --git a/src/core/server/saved_objects/saved_objects_service.mock.ts b/src/core/server/saved_objects/saved_objects_service.mock.ts index 292e39b0c199d..f1a1ce6c091b6 100644 --- a/src/core/server/saved_objects/saved_objects_service.mock.ts +++ b/src/core/server/saved_objects/saved_objects_service.mock.ts @@ -21,7 +21,7 @@ import type { import { savedObjectsRepositoryMock } from './service/lib/repository.mock'; import { savedObjectsClientMock } from './service/saved_objects_client.mock'; -import { typeRegistryMock } from './saved_objects_type_registry.mock'; +import { typeRegistryMock, serializerMock } from '@kbn/core-saved-objects-base-server-mocks'; import { savedObjectsExporterMock } from './export/saved_objects_exporter.mock'; import { savedObjectsImporterMock } from './import/saved_objects_importer.mock'; import { migrationMocks } from './migrations/mocks'; @@ -105,4 +105,5 @@ export const savedObjectsServiceMock = { createTypeRegistryMock: typeRegistryMock.create, createExporter: savedObjectsExporterMock.create, createImporter: savedObjectsImporterMock.create, + createSerializer: serializerMock.create, }; diff --git a/src/core/server/saved_objects/saved_objects_service.test.mocks.ts b/src/core/server/saved_objects/saved_objects_service.test.mocks.ts index f4b58fd12d8ba..4b66f5865def5 100644 --- a/src/core/server/saved_objects/saved_objects_service.test.mocks.ts +++ b/src/core/server/saved_objects/saved_objects_service.test.mocks.ts @@ -8,7 +8,7 @@ import { mockKibanaMigrator } from './migrations/kibana_migrator.mock'; import { savedObjectsClientProviderMock } from './service/lib/scoped_client_provider.mock'; -import { typeRegistryMock } from './saved_objects_type_registry.mock'; +import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; export const migratorInstanceMock = mockKibanaMigrator.create(); export const KibanaMigratorMock = jest.fn().mockImplementation(() => migratorInstanceMock); @@ -22,15 +22,15 @@ jest.doMock('./service/lib/scoped_client_provider', () => ({ })); export const typeRegistryInstanceMock = typeRegistryMock.create(); -jest.doMock('./saved_objects_type_registry', () => ({ - SavedObjectTypeRegistry: jest.fn().mockImplementation(() => typeRegistryInstanceMock), -})); +jest.doMock('@kbn/core-saved-objects-base-server-internal', () => { + const actual = jest.requireActual('@kbn/core-saved-objects-base-server-internal'); + return { + ...actual, + SavedObjectTypeRegistry: jest.fn().mockImplementation(() => typeRegistryInstanceMock), + }; +}); export const registerRoutesMock = jest.fn(); jest.doMock('./routes', () => ({ registerRoutes: registerRoutesMock, })); - -// The SavedObjectsSerializer imports SavedObjectUtils from the '../service' module, and that somehow breaks unit tests for the -// SavedObjectsService. To avoid this, we mock the entire './serialization' module, since we don't need it for these tests. -jest.mock('./serialization'); diff --git a/src/core/server/saved_objects/saved_objects_service.ts b/src/core/server/saved_objects/saved_objects_service.ts index 53eb376cc37b7..542a13e3a4cc0 100644 --- a/src/core/server/saved_objects/saved_objects_service.ts +++ b/src/core/server/saved_objects/saved_objects_service.ts @@ -27,18 +27,18 @@ import type { SavedObjectsClientWrapperFactory, ISavedObjectTypeRegistry, } from '@kbn/core-saved-objects-server'; +import { + SavedObjectConfig, + SavedObjectsSerializer, + SavedObjectTypeRegistry, + type SavedObjectsConfigType, + type SavedObjectsMigrationConfigType, +} from '@kbn/core-saved-objects-base-server-internal'; import { SavedObjectsClient, SavedObjectsClientProvider } from './service'; import { KibanaMigrator, IKibanaMigrator } from './migrations'; import { InternalCoreUsageDataSetup } from '../core_usage_data'; import { InternalDeprecationsServiceSetup } from '../deprecations'; -import { - SavedObjectsConfigType, - SavedObjectsMigrationConfigType, - SavedObjectConfig, -} from './saved_objects_config'; import { SavedObjectsRepository } from './service/lib/repository'; -import { SavedObjectTypeRegistry } from './saved_objects_type_registry'; -import { SavedObjectsSerializer } from './serialization'; import { SavedObjectsExporter } from './export'; import { SavedObjectsImporter } from './import'; import { registerRoutes } from './routes'; diff --git a/src/core/server/saved_objects/service/index.ts b/src/core/server/saved_objects/service/index.ts index 1ce638c4c0188..0737fc725d1a4 100644 --- a/src/core/server/saved_objects/service/index.ts +++ b/src/core/server/saved_objects/service/index.ts @@ -5,6 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -export { SavedObjectsErrorHelpers, SavedObjectsClientProvider, SavedObjectsUtils } from './lib'; + +export { SavedObjectsClientProvider } from './lib'; export type { SavedObjectsRepository, ISavedObjectsClientProvider } from './lib'; export { SavedObjectsClient } from './saved_objects_client'; diff --git a/src/core/server/saved_objects/service/lib/aggregations/validation.ts b/src/core/server/saved_objects/service/lib/aggregations/validation.ts index 76098d73306af..b3a6bbae5e956 100644 --- a/src/core/server/saved_objects/service/lib/aggregations/validation.ts +++ b/src/core/server/saved_objects/service/lib/aggregations/validation.ts @@ -10,7 +10,7 @@ import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { ObjectType } from '@kbn/config-schema'; import { isPlainObject, isArray } from 'lodash'; -import { IndexMapping } from '../../../mappings'; +import type { IndexMapping } from '@kbn/core-saved-objects-base-server-internal'; import { isObjectTypeAttribute, rewriteObjectTypeAttribute, diff --git a/src/core/server/saved_objects/service/lib/aggregations/validation_utils.ts b/src/core/server/saved_objects/service/lib/aggregations/validation_utils.ts index 0b2cc8e235c9c..5548ad4d57a5d 100644 --- a/src/core/server/saved_objects/service/lib/aggregations/validation_utils.ts +++ b/src/core/server/saved_objects/service/lib/aggregations/validation_utils.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { IndexMapping } from '../../../mappings'; +import type { IndexMapping } from '@kbn/core-saved-objects-base-server-internal'; import { fieldDefined, hasFilterKeyError } from '../filter_utils'; /** diff --git a/src/core/server/saved_objects/service/lib/collect_multi_namespace_references.test.ts b/src/core/server/saved_objects/service/lib/collect_multi_namespace_references.test.ts index d3cf76716e598..86e1e82d12a6f 100644 --- a/src/core/server/saved_objects/service/lib/collect_multi_namespace_references.test.ts +++ b/src/core/server/saved_objects/service/lib/collect_multi_namespace_references.test.ts @@ -17,15 +17,15 @@ import type { SavedObjectsCollectMultiNamespaceReferencesObject, SavedObjectsCollectMultiNamespaceReferencesOptions, } from '@kbn/core-saved-objects-api-server'; -import { typeRegistryMock } from '../../saved_objects_type_registry.mock'; -import { SavedObjectsSerializer } from '../../serialization'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal'; +import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import { ALIAS_OR_SHARED_ORIGIN_SEARCH_PER_PAGE, CollectMultiNamespaceReferencesParams, } from './collect_multi_namespace_references'; import { collectMultiNamespaceReferences } from './collect_multi_namespace_references'; import type { CreatePointInTimeFinderFn } from './point_in_time_finder'; -import { SavedObjectsErrorHelpers } from './errors'; const SPACES = ['default', 'another-space']; const VERSION_PROPS = { _seq_no: 1, _primary_term: 1 }; diff --git a/src/core/server/saved_objects/service/lib/collect_multi_namespace_references.ts b/src/core/server/saved_objects/service/lib/collect_multi_namespace_references.ts index b31f817e4c4d2..222b58d3a03e0 100644 --- a/src/core/server/saved_objects/service/lib/collect_multi_namespace_references.ts +++ b/src/core/server/saved_objects/service/lib/collect_multi_namespace_references.ts @@ -15,8 +15,8 @@ import type { SavedObjectReferenceWithContext, } from '@kbn/core-saved-objects-api-server'; import type { ISavedObjectTypeRegistry } from '@kbn/core-saved-objects-server'; -import type { SavedObjectsSerializer } from '../../serialization'; -import { SavedObjectsErrorHelpers } from './errors'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import type { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal'; import { findLegacyUrlAliases } from './legacy_url_aliases'; import { getRootFields } from './included_fields'; import { diff --git a/src/core/server/saved_objects/service/lib/decorate_es_error.test.ts b/src/core/server/saved_objects/service/lib/decorate_es_error.test.ts index a7aa52f7a0f5a..8290f7345190d 100644 --- a/src/core/server/saved_objects/service/lib/decorate_es_error.test.ts +++ b/src/core/server/saved_objects/service/lib/decorate_es_error.test.ts @@ -8,8 +8,8 @@ import { errors as esErrors } from '@elastic/elasticsearch'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; import { decorateEsError } from './decorate_es_error'; -import { SavedObjectsErrorHelpers } from './errors'; describe('savedObjectsClient/decorateEsError', () => { it('always returns the same error it receives', () => { diff --git a/src/core/server/saved_objects/service/lib/decorate_es_error.ts b/src/core/server/saved_objects/service/lib/decorate_es_error.ts index 40eda7a854a25..9cfdffc13a5dd 100644 --- a/src/core/server/saved_objects/service/lib/decorate_es_error.ts +++ b/src/core/server/saved_objects/service/lib/decorate_es_error.ts @@ -10,6 +10,7 @@ import { get } from 'lodash'; import { errors as esErrors } from '@elastic/elasticsearch'; import type { ElasticsearchErrorDetails } from '@kbn/es-errors'; import { isSupportedEsServer } from '@kbn/core-elasticsearch-server-internal'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; const responseErrors = { isServiceUnavailable: (statusCode?: number) => statusCode === 503, @@ -25,8 +26,6 @@ const { ConnectionError, NoLivingConnectionsError, TimeoutError } = esErrors; const SCRIPT_CONTEXT_DISABLED_REGEX = /(?:cannot execute scripts using \[)([a-z]*)(?:\] context)/; const INLINE_SCRIPTS_DISABLED_MESSAGE = 'cannot execute [inline] scripts'; -import { SavedObjectsErrorHelpers } from './errors'; - type EsErrors = | esErrors.ConnectionError | esErrors.NoLivingConnectionsError diff --git a/src/core/server/saved_objects/service/lib/filter_utils.ts b/src/core/server/saved_objects/service/lib/filter_utils.ts index 30880368d096d..ae7bb08039850 100644 --- a/src/core/server/saved_objects/service/lib/filter_utils.ts +++ b/src/core/server/saved_objects/service/lib/filter_utils.ts @@ -9,8 +9,8 @@ import { set } from '@kbn/safer-lodash-set'; import { get, cloneDeep } from 'lodash'; import * as esKuery from '@kbn/es-query'; -import { SavedObjectsErrorHelpers } from './errors'; -import { IndexMapping } from '../../mappings'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; +import type { IndexMapping } from '@kbn/core-saved-objects-base-server-internal'; type KueryNode = any; diff --git a/src/core/server/saved_objects/service/lib/find_shared_origin_objects.ts b/src/core/server/saved_objects/service/lib/find_shared_origin_objects.ts index 229e0c6f90a66..34aff0b11d6d0 100644 --- a/src/core/server/saved_objects/service/lib/find_shared_origin_objects.ts +++ b/src/core/server/saved_objects/service/lib/find_shared_origin_objects.ts @@ -7,9 +7,9 @@ */ import * as esKuery from '@kbn/es-query'; +import { ALL_NAMESPACES_STRING } from '@kbn/core-saved-objects-utils-server'; import { getObjectKey } from './internal_utils'; import type { CreatePointInTimeFinderFn } from './point_in_time_finder'; -import { ALL_NAMESPACES_STRING } from './utils'; interface ObjectOrigin { /** The object's type. */ diff --git a/src/core/server/saved_objects/service/lib/get_index_for_type.test.ts b/src/core/server/saved_objects/service/lib/get_index_for_type.test.ts index 16e3ba9495f04..01bb3174cc56b 100644 --- a/src/core/server/saved_objects/service/lib/get_index_for_type.test.ts +++ b/src/core/server/saved_objects/service/lib/get_index_for_type.test.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ +import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import { getIndexForType } from './get_index_for_type'; -import { typeRegistryMock } from '../../saved_objects_type_registry.mock'; describe('getIndexForType', () => { const kibanaVersion = '8.0.0'; diff --git a/src/core/server/saved_objects/service/lib/index.ts b/src/core/server/saved_objects/service/lib/index.ts index 8fdb8301ce101..a43750608bf95 100644 --- a/src/core/server/saved_objects/service/lib/index.ts +++ b/src/core/server/saved_objects/service/lib/index.ts @@ -11,8 +11,4 @@ export { SavedObjectsClientProvider } from './scoped_client_provider'; export type { ISavedObjectsClientProvider } from './scoped_client_provider'; -export { SavedObjectsErrorHelpers } from './errors'; - -export { SavedObjectsUtils } from './utils'; - export { getIndexForType } from './get_index_for_type'; diff --git a/src/core/server/saved_objects/service/lib/internal_bulk_resolve.test.ts b/src/core/server/saved_objects/service/lib/internal_bulk_resolve.test.ts index ec55ea7725718..119b3ac85b698 100644 --- a/src/core/server/saved_objects/service/lib/internal_bulk_resolve.test.ts +++ b/src/core/server/saved_objects/service/lib/internal_bulk_resolve.test.ts @@ -18,12 +18,13 @@ import type { SavedObjectsBulkResolveObject, SavedObjectsBaseOptions, } from '@kbn/core-saved-objects-api-server'; -import { LEGACY_URL_ALIAS_TYPE } from '../../object_types'; -import { typeRegistryMock } from '../../saved_objects_type_registry.mock'; -import { SavedObjectsSerializer } from '../../serialization'; -import { SavedObjectsErrorHelpers } from './errors'; +import { SavedObjectsErrorHelpers, SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server'; +import { + SavedObjectsSerializer, + LEGACY_URL_ALIAS_TYPE, +} from '@kbn/core-saved-objects-base-server-internal'; +import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import { internalBulkResolve, InternalBulkResolveParams } from './internal_bulk_resolve'; -import { SavedObjectsUtils } from './utils'; import { normalizeNamespace } from './internal_utils'; const VERSION_PROPS = { _seq_no: 1, _primary_term: 1 }; diff --git a/src/core/server/saved_objects/service/lib/internal_bulk_resolve.ts b/src/core/server/saved_objects/service/lib/internal_bulk_resolve.ts index ebe94e4e95b4d..b15fc56e2f3c0 100644 --- a/src/core/server/saved_objects/service/lib/internal_bulk_resolve.ts +++ b/src/core/server/saved_objects/service/lib/internal_bulk_resolve.ts @@ -21,14 +21,20 @@ import type { ISavedObjectTypeRegistry, SavedObjectsRawDocSource, } from '@kbn/core-saved-objects-server'; +import { + SavedObjectsErrorHelpers, + type DecoratedError, +} from '@kbn/core-saved-objects-utils-server'; +import { + LEGACY_URL_ALIAS_TYPE, + type LegacyUrlAlias, + type SavedObjectsSerializer, +} from '@kbn/core-saved-objects-base-server-internal'; import { CORE_USAGE_STATS_ID, CORE_USAGE_STATS_TYPE, REPOSITORY_RESOLVE_OUTCOME_STATS, } from '../../../core_usage_data'; -import { LegacyUrlAlias, LEGACY_URL_ALIAS_TYPE } from '../../object_types'; -import type { SavedObjectsSerializer } from '../../serialization'; -import { DecoratedError, SavedObjectsErrorHelpers } from './errors'; import { getCurrentTime, getSavedObjectFromSource, diff --git a/src/core/server/saved_objects/service/lib/internal_utils.test.ts b/src/core/server/saved_objects/service/lib/internal_utils.test.ts index 65cb83d3e8a77..96ca3949a4775 100644 --- a/src/core/server/saved_objects/service/lib/internal_utils.test.ts +++ b/src/core/server/saved_objects/service/lib/internal_utils.test.ts @@ -7,8 +7,9 @@ */ import type { SavedObjectsRawDoc } from '@kbn/core-saved-objects-server'; -import { typeRegistryMock } from '../../saved_objects_type_registry.mock'; -import { encodeHitVersion } from '../../version'; +import { ALL_NAMESPACES_STRING } from '@kbn/core-saved-objects-utils-server'; +import { encodeHitVersion } from '@kbn/core-saved-objects-base-server-internal'; +import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import { getBulkOperationError, getCurrentTime, @@ -19,7 +20,6 @@ import { rawDocExistsInNamespace, rawDocExistsInNamespaces, } from './internal_utils'; -import { ALL_NAMESPACES_STRING } from './utils'; describe('#getBulkOperationError', () => { const type = 'obj-type'; diff --git a/src/core/server/saved_objects/service/lib/internal_utils.ts b/src/core/server/saved_objects/service/lib/internal_utils.ts index 5f20a2bc3c842..84539510504a2 100644 --- a/src/core/server/saved_objects/service/lib/internal_utils.ts +++ b/src/core/server/saved_objects/service/lib/internal_utils.ts @@ -13,9 +13,15 @@ import type { SavedObjectsRawDoc, SavedObjectsRawDocSource, } from '@kbn/core-saved-objects-server'; -import { decodeRequestVersion, encodeHitVersion } from '../../version'; -import { SavedObjectsErrorHelpers } from './errors'; -import { ALL_NAMESPACES_STRING, SavedObjectsUtils } from './utils'; +import { + SavedObjectsErrorHelpers, + SavedObjectsUtils, + ALL_NAMESPACES_STRING, +} from '@kbn/core-saved-objects-utils-server'; +import { + decodeRequestVersion, + encodeHitVersion, +} from '@kbn/core-saved-objects-base-server-internal'; /** * Discriminated union (TypeScript approximation of an algebraic data type); this design pattern is used for internal repository operations. diff --git a/src/core/server/saved_objects/service/lib/legacy_url_aliases/delete_legacy_url_aliases.test.ts b/src/core/server/saved_objects/service/lib/legacy_url_aliases/delete_legacy_url_aliases.test.ts index dc261ee23aafb..77a538672ff19 100644 --- a/src/core/server/saved_objects/service/lib/legacy_url_aliases/delete_legacy_url_aliases.test.ts +++ b/src/core/server/saved_objects/service/lib/legacy_url_aliases/delete_legacy_url_aliases.test.ts @@ -11,8 +11,8 @@ import { mockGetEsErrorMessage } from './delete_legacy_url_aliases.test.mock'; / import { errors as EsErrors } from '@elastic/elasticsearch'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; -import { typeRegistryMock } from '../../../saved_objects_type_registry.mock'; -import { ALL_NAMESPACES_STRING } from '../utils'; +import { ALL_NAMESPACES_STRING } from '@kbn/core-saved-objects-utils-server'; +import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import { deleteLegacyUrlAliases } from './delete_legacy_url_aliases'; import type { DeleteLegacyUrlAliasesParams } from './delete_legacy_url_aliases'; diff --git a/src/core/server/saved_objects/service/lib/legacy_url_aliases/delete_legacy_url_aliases.ts b/src/core/server/saved_objects/service/lib/legacy_url_aliases/delete_legacy_url_aliases.ts index 873f4ab9b6c9c..73489308f59af 100644 --- a/src/core/server/saved_objects/service/lib/legacy_url_aliases/delete_legacy_url_aliases.ts +++ b/src/core/server/saved_objects/service/lib/legacy_url_aliases/delete_legacy_url_aliases.ts @@ -10,11 +10,13 @@ import * as esKuery from '@kbn/es-query'; import { getErrorMessage as getEsErrorMessage } from '@kbn/core-elasticsearch-client-server-internal'; import type { ISavedObjectTypeRegistry } from '@kbn/core-saved-objects-server'; -import type { IndexMapping } from '../../../mappings'; -import { LEGACY_URL_ALIAS_TYPE } from '../../../object_types'; +import { ALL_NAMESPACES_STRING } from '@kbn/core-saved-objects-utils-server'; +import { + LEGACY_URL_ALIAS_TYPE, + type IndexMapping, +} from '@kbn/core-saved-objects-base-server-internal'; import type { RepositoryEsClient } from '../repository_es_client'; import { getSearchDsl } from '../search_dsl'; -import { ALL_NAMESPACES_STRING } from '../utils'; /** @internal */ export interface DeleteLegacyUrlAliasesParams { diff --git a/src/core/server/saved_objects/service/lib/legacy_url_aliases/find_legacy_url_aliases.test.ts b/src/core/server/saved_objects/service/lib/legacy_url_aliases/find_legacy_url_aliases.test.ts index 8200c7502fec6..36435b9828be4 100644 --- a/src/core/server/saved_objects/service/lib/legacy_url_aliases/find_legacy_url_aliases.test.ts +++ b/src/core/server/saved_objects/service/lib/legacy_url_aliases/find_legacy_url_aliases.test.ts @@ -9,7 +9,10 @@ import type { DeeplyMockedKeys } from '@kbn/utility-types-jest'; import type { ISavedObjectsRepository } from '@kbn/core-saved-objects-api-server'; -import { LegacyUrlAlias, LEGACY_URL_ALIAS_TYPE } from '../../../object_types'; +import { + type LegacyUrlAlias, + LEGACY_URL_ALIAS_TYPE, +} from '@kbn/core-saved-objects-base-server-internal'; import type { CreatePointInTimeFinderFn, PointInTimeFinder } from '../point_in_time_finder'; import { savedObjectsPointInTimeFinderMock } from '../point_in_time_finder.mock'; import { savedObjectsRepositoryMock } from '../repository.mock'; diff --git a/src/core/server/saved_objects/service/lib/legacy_url_aliases/find_legacy_url_aliases.ts b/src/core/server/saved_objects/service/lib/legacy_url_aliases/find_legacy_url_aliases.ts index 70b1730ec8f48..5a90a2e70d073 100644 --- a/src/core/server/saved_objects/service/lib/legacy_url_aliases/find_legacy_url_aliases.ts +++ b/src/core/server/saved_objects/service/lib/legacy_url_aliases/find_legacy_url_aliases.ts @@ -7,7 +7,10 @@ */ import * as esKuery from '@kbn/es-query'; -import { LegacyUrlAlias, LEGACY_URL_ALIAS_TYPE } from '../../../object_types'; +import { + type LegacyUrlAlias, + LEGACY_URL_ALIAS_TYPE, +} from '@kbn/core-saved-objects-base-server-internal'; import { getObjectKey } from '../internal_utils'; import type { CreatePointInTimeFinderFn } from '../point_in_time_finder'; diff --git a/src/core/server/saved_objects/service/lib/preflight_check_for_create.test.ts b/src/core/server/saved_objects/service/lib/preflight_check_for_create.test.ts index 9563ebd51344f..d23d2cf5e804e 100644 --- a/src/core/server/saved_objects/service/lib/preflight_check_for_create.test.ts +++ b/src/core/server/saved_objects/service/lib/preflight_check_for_create.test.ts @@ -15,9 +15,11 @@ import type { DeeplyMockedKeys } from '@kbn/utility-types-jest'; import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; -import { LEGACY_URL_ALIAS_TYPE } from '../../object_types'; -import { typeRegistryMock } from '../../saved_objects_type_registry.mock'; -import { SavedObjectsSerializer } from '../../serialization'; +import { + SavedObjectsSerializer, + LEGACY_URL_ALIAS_TYPE, +} from '@kbn/core-saved-objects-base-server-internal'; +import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import type { CreatePointInTimeFinderFn } from './point_in_time_finder'; import { ALIAS_SEARCH_PER_PAGE, diff --git a/src/core/server/saved_objects/service/lib/preflight_check_for_create.ts b/src/core/server/saved_objects/service/lib/preflight_check_for_create.ts index b94302b2b990f..a3fd4218c3eeb 100644 --- a/src/core/server/saved_objects/service/lib/preflight_check_for_create.ts +++ b/src/core/server/saved_objects/service/lib/preflight_check_for_create.ts @@ -13,15 +13,20 @@ import type { SavedObjectsRawDoc, SavedObjectsRawDocSource, } from '@kbn/core-saved-objects-server'; -import { LegacyUrlAlias, LEGACY_URL_ALIAS_TYPE } from '../../object_types'; -import type { SavedObjectsSerializer } from '../../serialization'; +import { + SavedObjectsErrorHelpers, + ALL_NAMESPACES_STRING, +} from '@kbn/core-saved-objects-utils-server'; +import { + LEGACY_URL_ALIAS_TYPE, + type LegacyUrlAlias, + type SavedObjectsSerializer, +} from '@kbn/core-saved-objects-base-server-internal'; import { findLegacyUrlAliases } from './legacy_url_aliases'; import { Either, rawDocExistsInNamespaces } from './internal_utils'; import { getObjectKey, isLeft, isRight } from './internal_utils'; import type { CreatePointInTimeFinderFn } from './point_in_time_finder'; import type { RepositoryEsClient } from './repository_es_client'; -import { ALL_NAMESPACES_STRING } from './utils'; -import { SavedObjectsErrorHelpers } from './errors'; /** * If the object will be created in this many spaces (or "*" all current and future spaces), we use find to fetch all aliases. diff --git a/src/core/server/saved_objects/service/lib/repository.test.ts b/src/core/server/saved_objects/service/lib/repository.test.ts index 9af620101ab94..30a6b1ab36151 100644 --- a/src/core/server/saved_objects/service/lib/repository.test.ts +++ b/src/core/server/saved_objects/service/lib/repository.test.ts @@ -55,17 +55,21 @@ import type { SavedObjectsMappingProperties, SavedObjectsTypeMappingDefinition, } from '@kbn/core-saved-objects-server'; +import { + SavedObjectsErrorHelpers, + ALL_NAMESPACES_STRING, +} from '@kbn/core-saved-objects-utils-server'; import { SavedObjectsRepository } from './repository'; -import { SavedObjectsErrorHelpers } from './errors'; import { PointInTimeFinder } from './point_in_time_finder'; -import { ALL_NAMESPACES_STRING } from './utils'; import { loggerMock } from '@kbn/logging-mocks'; -import { SavedObjectsSerializer } from '../../serialization'; -import { encodeHitVersion } from '../../version'; -import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry'; +import { + SavedObjectTypeRegistry, + SavedObjectsSerializer, + encodeHitVersion, + LEGACY_URL_ALIAS_TYPE, +} from '@kbn/core-saved-objects-base-server-internal'; import { DocumentMigrator } from '../../migrations/core/document_migrator'; import { mockKibanaMigrator } from '../../migrations/kibana_migrator.mock'; -import { LEGACY_URL_ALIAS_TYPE } from '../../object_types'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; import * as esKuery from '@kbn/es-query'; import { errors as EsErrors } from '@elastic/elasticsearch'; diff --git a/src/core/server/saved_objects/service/lib/repository.ts b/src/core/server/saved_objects/service/lib/repository.ts index 1ede13e1142b2..524fe5f7eb966 100644 --- a/src/core/server/saved_objects/service/lib/repository.ts +++ b/src/core/server/saved_objects/service/lib/repository.ts @@ -60,17 +60,31 @@ import type { SavedObjectsRawDocSource, ISavedObjectTypeRegistry, } from '@kbn/core-saved-objects-server'; -import { getRootPropertiesObjects, IndexMapping } from '../../mappings'; +import { + SavedObjectsErrorHelpers, + type DecoratedError, +} from '@kbn/core-saved-objects-utils-server'; +import { + ALL_NAMESPACES_STRING, + FIND_DEFAULT_PAGE, + FIND_DEFAULT_PER_PAGE, + SavedObjectsUtils, +} from '@kbn/core-saved-objects-utils-server'; +import { + SavedObjectsSerializer, + SavedObjectsTypeValidator, + decodeRequestVersion, + encodeVersion, + encodeHitVersion, + getRootPropertiesObjects, + LEGACY_URL_ALIAS_TYPE, + type IndexMapping, +} from '@kbn/core-saved-objects-base-server-internal'; import { PointInTimeFinder } from './point_in_time_finder'; import { createRepositoryEsClient, RepositoryEsClient } from './repository_es_client'; import { getSearchDsl } from './search_dsl'; import { includedFields } from './included_fields'; -import { SavedObjectsErrorHelpers, DecoratedError } from './errors'; -import { decodeRequestVersion, encodeVersion, encodeHitVersion } from '../../version'; import { IKibanaMigrator } from '../../migrations'; -import { SavedObjectsSerializer } from '../../serialization'; -import { LEGACY_URL_ALIAS_TYPE } from '../../object_types'; -import { SavedObjectsTypeValidator } from '../../validation'; import { internalBulkResolve, InternalBulkResolveError } from './internal_bulk_resolve'; import { validateConvertFilterToKueryNode } from './filter_utils'; import { validateAndConvertAggregations } from './aggregations'; @@ -86,12 +100,6 @@ import { isLeft, isRight, } from './internal_utils'; -import { - ALL_NAMESPACES_STRING, - FIND_DEFAULT_PAGE, - FIND_DEFAULT_PER_PAGE, - SavedObjectsUtils, -} from './utils'; import { collectMultiNamespaceReferences } from './collect_multi_namespace_references'; import { updateObjectsSpaces } from './update_objects_spaces'; import { getIndexForType } from './get_index_for_type'; diff --git a/src/core/server/saved_objects/service/lib/repository_create_repository.test.ts b/src/core/server/saved_objects/service/lib/repository_create_repository.test.ts index b1033012a4beb..9a2d56cdaaa89 100644 --- a/src/core/server/saved_objects/service/lib/repository_create_repository.test.ts +++ b/src/core/server/saved_objects/service/lib/repository_create_repository.test.ts @@ -6,10 +6,10 @@ * Side Public License, v 1. */ +import { SavedObjectTypeRegistry } from '@kbn/core-saved-objects-base-server-internal'; import { SavedObjectsRepository } from './repository'; import { mockKibanaMigrator } from '../../migrations/kibana_migrator.mock'; import { KibanaMigrator } from '../../migrations'; -import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry'; import { loggerMock, MockedLogger } from '@kbn/logging-mocks'; jest.mock('./repository'); diff --git a/src/core/server/saved_objects/service/lib/repository_es_client.test.ts b/src/core/server/saved_objects/service/lib/repository_es_client.test.ts index f465ffb6a3dab..cbd8f81bfc0f5 100644 --- a/src/core/server/saved_objects/service/lib/repository_es_client.test.ts +++ b/src/core/server/saved_objects/service/lib/repository_es_client.test.ts @@ -10,7 +10,7 @@ import { retryCallClusterMock } from './repository_es_client.test.mock'; import { createRepositoryEsClient, RepositoryEsClient } from './repository_es_client'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; -import { SavedObjectsErrorHelpers } from './errors'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; describe('RepositoryEsClient', () => { let client: ReturnType; diff --git a/src/core/server/saved_objects/service/lib/scoped_client_provider.test.ts b/src/core/server/saved_objects/service/lib/scoped_client_provider.test.ts index efc04f69e6ecf..2641ecfb1cec6 100644 --- a/src/core/server/saved_objects/service/lib/scoped_client_provider.test.ts +++ b/src/core/server/saved_objects/service/lib/scoped_client_provider.test.ts @@ -7,8 +7,8 @@ */ import { httpServerMock } from '@kbn/core-http-server-mocks'; +import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import { SavedObjectsClientProvider } from './scoped_client_provider'; -import { typeRegistryMock } from '../../saved_objects_type_registry.mock'; test(`uses default client factory when one isn't set`, () => { const returnValue = Symbol(); diff --git a/src/core/server/saved_objects/service/lib/search_dsl/query_params.test.ts b/src/core/server/saved_objects/service/lib/search_dsl/query_params.test.ts index 3f27bef5c68af..c502665468e6c 100644 --- a/src/core/server/saved_objects/service/lib/search_dsl/query_params.test.ts +++ b/src/core/server/saved_objects/service/lib/search_dsl/query_params.test.ts @@ -12,8 +12,11 @@ import * as esKuery from '@kbn/es-query'; type KueryNode = any; -import { ALL_NAMESPACES_STRING, DEFAULT_NAMESPACE_STRING } from '../utils'; -import { SavedObjectTypeRegistry } from '../../../saved_objects_type_registry'; +import { + ALL_NAMESPACES_STRING, + DEFAULT_NAMESPACE_STRING, +} from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectTypeRegistry } from '@kbn/core-saved-objects-base-server-internal'; import { getQueryParams } from './query_params'; const registerTypes = (registry: SavedObjectTypeRegistry) => { diff --git a/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts b/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts index 1c21a7177faf4..669f2a273569b 100644 --- a/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts +++ b/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts @@ -11,7 +11,10 @@ import * as esKuery from '@kbn/es-query'; type KueryNode = any; import type { ISavedObjectTypeRegistry } from '@kbn/core-saved-objects-server'; -import { ALL_NAMESPACES_STRING, DEFAULT_NAMESPACE_STRING } from '../utils'; +import { + ALL_NAMESPACES_STRING, + DEFAULT_NAMESPACE_STRING, +} from '@kbn/core-saved-objects-utils-server'; import { getReferencesFilter } from './references_filter'; /** diff --git a/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.test.ts b/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.test.ts index b15560b82ab31..d1ed7251b2414 100644 --- a/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.test.ts +++ b/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.test.ts @@ -10,7 +10,7 @@ jest.mock('./pit_params'); jest.mock('./query_params'); jest.mock('./sorting_params'); -import { typeRegistryMock } from '../../../saved_objects_type_registry.mock'; +import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import * as pitParamsNS from './pit_params'; import * as queryParamsNS from './query_params'; import { getSearchDsl } from './search_dsl'; diff --git a/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.ts b/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.ts index 7d6d31949739c..980bf800755b9 100644 --- a/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.ts +++ b/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.ts @@ -11,7 +11,7 @@ import Boom from '@hapi/boom'; import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { SavedObjectsPitParams } from '@kbn/core-saved-objects-api-server'; import type { ISavedObjectTypeRegistry } from '@kbn/core-saved-objects-server'; -import { IndexMapping } from '../../../mappings'; +import type { IndexMapping } from '@kbn/core-saved-objects-base-server-internal'; import { getQueryParams, HasReferenceQueryParams, SearchOperator } from './query_params'; import { getPitParams } from './pit_params'; import { getSortingParams } from './sorting_params'; diff --git a/src/core/server/saved_objects/service/lib/search_dsl/sorting_params.ts b/src/core/server/saved_objects/service/lib/search_dsl/sorting_params.ts index 030219b4ba5b1..d2308736b5dc2 100644 --- a/src/core/server/saved_objects/service/lib/search_dsl/sorting_params.ts +++ b/src/core/server/saved_objects/service/lib/search_dsl/sorting_params.ts @@ -8,7 +8,7 @@ import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import Boom from '@hapi/boom'; -import { getProperty, IndexMapping } from '../../../mappings'; +import { getProperty, type IndexMapping } from '@kbn/core-saved-objects-base-server-internal'; const TOP_LEVEL_FIELDS = ['_id', '_score']; diff --git a/src/core/server/saved_objects/service/lib/update_objects_spaces.test.ts b/src/core/server/saved_objects/service/lib/update_objects_spaces.test.ts index dfd1600e6d218..6e6c241965056 100644 --- a/src/core/server/saved_objects/service/lib/update_objects_spaces.test.ts +++ b/src/core/server/saved_objects/service/lib/update_objects_spaces.test.ts @@ -17,12 +17,14 @@ import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-m import { loggerMock } from '@kbn/logging-mocks'; import type { SavedObjectsUpdateObjectsSpacesObject } from '@kbn/core-saved-objects-api-server'; -import { typeRegistryMock } from '../../saved_objects_type_registry.mock'; -import { SavedObjectsSerializer } from '../../serialization'; +import { + SavedObjectsErrorHelpers, + ALL_NAMESPACES_STRING, +} from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal'; +import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks'; import type { UpdateObjectsSpacesParams } from './update_objects_spaces'; import { updateObjectsSpaces } from './update_objects_spaces'; -import { ALL_NAMESPACES_STRING } from './utils'; -import { SavedObjectsErrorHelpers } from './errors'; type SetupParams = Partial< Pick diff --git a/src/core/server/saved_objects/service/lib/update_objects_spaces.ts b/src/core/server/saved_objects/service/lib/update_objects_spaces.ts index a92f9a952af18..4053fcff4a8ea 100644 --- a/src/core/server/saved_objects/service/lib/update_objects_spaces.ts +++ b/src/core/server/saved_objects/service/lib/update_objects_spaces.ts @@ -22,10 +22,15 @@ import type { ISavedObjectTypeRegistry, SavedObjectsRawDocSource, } from '@kbn/core-saved-objects-server'; -import type { IndexMapping } from '../../mappings'; -import type { SavedObjectsSerializer } from '../../serialization'; -import type { DecoratedError } from './errors'; -import { SavedObjectsErrorHelpers } from './errors'; +import { + SavedObjectsErrorHelpers, + ALL_NAMESPACES_STRING, + type DecoratedError, +} from '@kbn/core-saved-objects-utils-server'; +import type { + IndexMapping, + SavedObjectsSerializer, +} from '@kbn/core-saved-objects-base-server-internal'; import { getBulkOperationError, getExpectedVersionProperties, @@ -36,7 +41,6 @@ import { } from './internal_utils'; import { DEFAULT_REFRESH_SETTING } from './repository'; import type { RepositoryEsClient } from './repository_es_client'; -import { ALL_NAMESPACES_STRING } from './utils'; import type { DeleteLegacyUrlAliasesParams } from './legacy_url_aliases'; import { deleteLegacyUrlAliases } from './legacy_url_aliases'; diff --git a/src/core/server/saved_objects/service/saved_objects_client.mock.ts b/src/core/server/saved_objects/service/saved_objects_client.mock.ts index fe5ea92ed0766..55a96ea3cecfc 100644 --- a/src/core/server/saved_objects/service/saved_objects_client.mock.ts +++ b/src/core/server/saved_objects/service/saved_objects_client.mock.ts @@ -7,7 +7,7 @@ */ import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; -import { SavedObjectsErrorHelpers } from './lib/errors'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; import { savedObjectsPointInTimeFinderMock } from './lib/point_in_time_finder.mock'; const create = () => { diff --git a/src/core/server/saved_objects/service/saved_objects_client.ts b/src/core/server/saved_objects/service/saved_objects_client.ts index c9cfd9e720056..7c2b3a205b76d 100644 --- a/src/core/server/saved_objects/service/saved_objects_client.ts +++ b/src/core/server/saved_objects/service/saved_objects_client.ts @@ -40,7 +40,7 @@ import type { SavedObjectsCreatePointInTimeFinderOptions, SavedObjectsFindOptions, } from '@kbn/core-saved-objects-api-server'; -import { SavedObjectsErrorHelpers } from './lib/errors'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; /** * Core internal implementation of {@link SavedObjectsClientContract} diff --git a/src/core/server/server.ts b/src/core/server/server.ts index 42878ca66e614..82705f2428e5c 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -45,6 +45,10 @@ import { import { MetricsService, opsConfig } from '@kbn/core-metrics-server-internal'; import { CapabilitiesService } from '@kbn/core-capabilities-server-internal'; import type { SavedObjectsServiceStart } from '@kbn/core-saved-objects-server'; +import { + savedObjectsConfig, + savedObjectsMigrationConfig, +} from '@kbn/core-saved-objects-base-server-internal'; import { CoreApp } from './core_app'; import { I18nService } from './i18n'; import { HttpResourcesService } from './http_resources'; @@ -53,9 +57,8 @@ import { UiSettingsService } from './ui_settings'; import { PluginsService, config as pluginsConfig } from './plugins'; import { SavedObjectsService } from './saved_objects'; // do not try to shorten the import to `./status`, it will break server test mocking -import { StatusService } from './status/status_service'; -import { savedObjectsConfig, savedObjectsMigrationConfig } from './saved_objects'; +import { StatusService } from './status/status_service'; import { config as uiSettingsConfig } from './ui_settings'; import { config as statusConfig } from './status'; import { config as i18nConfig } from './i18n'; diff --git a/src/core/server/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.test.ts b/src/core/server/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.test.ts index c5a9c9729b78a..6102ff6f7b1be 100644 --- a/src/core/server/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.test.ts +++ b/src/core/server/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.test.ts @@ -10,7 +10,7 @@ import { mockTransform, mockGetUpgradeableConfig, } from './create_or_upgrade_saved_config.test.mock'; -import { SavedObjectsErrorHelpers } from '../../saved_objects'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; import { savedObjectsClientMock } from '../../saved_objects/service/saved_objects_client.mock'; import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; diff --git a/src/core/server/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.ts b/src/core/server/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.ts index def1b58784174..e453c9cf46f31 100644 --- a/src/core/server/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.ts +++ b/src/core/server/ui_settings/create_or_upgrade_saved_config/create_or_upgrade_saved_config.ts @@ -11,7 +11,7 @@ import { defaults } from 'lodash'; import type { Logger, LogMeta } from '@kbn/logging'; import { asyncForEach } from '@kbn/std'; import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; -import { SavedObjectsErrorHelpers } from '../../saved_objects'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; import { getUpgradeableConfig } from './get_upgradeable_config'; import { transforms } from '../saved_objects'; diff --git a/src/core/server/ui_settings/routes/delete.ts b/src/core/server/ui_settings/routes/delete.ts index 705a5319c1cf5..4147358d4e1ee 100644 --- a/src/core/server/ui_settings/routes/delete.ts +++ b/src/core/server/ui_settings/routes/delete.ts @@ -8,7 +8,7 @@ import { schema } from '@kbn/config-schema'; -import { SavedObjectsErrorHelpers } from '../../saved_objects'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; import type { InternalUiSettingsRouter } from '../internal_types'; import { CannotOverrideError } from '../ui_settings_errors'; diff --git a/src/core/server/ui_settings/routes/get.ts b/src/core/server/ui_settings/routes/get.ts index c940c2e1fe71e..2603526c37503 100644 --- a/src/core/server/ui_settings/routes/get.ts +++ b/src/core/server/ui_settings/routes/get.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; import type { InternalUiSettingsRouter } from '../internal_types'; -import { SavedObjectsErrorHelpers } from '../../saved_objects'; export function registerGetRoute(router: InternalUiSettingsRouter) { router.get( diff --git a/src/core/server/ui_settings/routes/set.ts b/src/core/server/ui_settings/routes/set.ts index af62fda0144b6..61493be876ad7 100644 --- a/src/core/server/ui_settings/routes/set.ts +++ b/src/core/server/ui_settings/routes/set.ts @@ -8,7 +8,7 @@ import { schema, ValidationError } from '@kbn/config-schema'; -import { SavedObjectsErrorHelpers } from '../../saved_objects'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; import type { InternalUiSettingsRouter } from '../internal_types'; import { CannotOverrideError } from '../ui_settings_errors'; diff --git a/src/core/server/ui_settings/routes/set_many.ts b/src/core/server/ui_settings/routes/set_many.ts index fe0ee1a0a721f..3bbe14c4a0076 100644 --- a/src/core/server/ui_settings/routes/set_many.ts +++ b/src/core/server/ui_settings/routes/set_many.ts @@ -8,7 +8,7 @@ import { schema, ValidationError } from '@kbn/config-schema'; -import { SavedObjectsErrorHelpers } from '../../saved_objects'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; import type { InternalUiSettingsRouter } from '../internal_types'; import { CannotOverrideError } from '../ui_settings_errors'; diff --git a/src/core/server/ui_settings/saved_objects/transforms.test.ts b/src/core/server/ui_settings/saved_objects/transforms.test.ts index afcd525673aa0..279dfd637b9e2 100644 --- a/src/core/server/ui_settings/saved_objects/transforms.test.ts +++ b/src/core/server/ui_settings/saved_objects/transforms.test.ts @@ -7,7 +7,7 @@ */ import { savedObjectsClientMock } from '../../mocks'; -import { SavedObjectsErrorHelpers } from '../../saved_objects'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; import { SavedObject } from '../../types'; import type { UpgradeableConfigAttributes } from '../create_or_upgrade_saved_config'; import { transformDefaultIndex } from './transforms'; diff --git a/src/core/server/ui_settings/saved_objects/transforms.ts b/src/core/server/ui_settings/saved_objects/transforms.ts index cabf14a2e6469..797b895f91556 100644 --- a/src/core/server/ui_settings/saved_objects/transforms.ts +++ b/src/core/server/ui_settings/saved_objects/transforms.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { SavedObjectsErrorHelpers } from '../../saved_objects'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; import type { SavedObjectsClientContract } from '../../types'; import type { UpgradeableConfigAttributes } from '../create_or_upgrade_saved_config'; diff --git a/src/core/server/ui_settings/ui_settings_client.ts b/src/core/server/ui_settings/ui_settings_client.ts index 5175af54ffe3e..aa7d344881cf6 100644 --- a/src/core/server/ui_settings/ui_settings_client.ts +++ b/src/core/server/ui_settings/ui_settings_client.ts @@ -8,7 +8,7 @@ import type { Logger } from '@kbn/logging'; import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; -import { SavedObjectsErrorHelpers } from '../saved_objects'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-utils-server'; import { createOrUpgradeSavedConfig } from './create_or_upgrade_saved_config'; import { UiSettingsParams } from './types'; import { CannotOverrideError } from './ui_settings_errors'; diff --git a/src/dev/build/tasks/clean_tasks.ts b/src/dev/build/tasks/clean_tasks.ts index 6184c568df07d..15b20e712334c 100644 --- a/src/dev/build/tasks/clean_tasks.ts +++ b/src/dev/build/tasks/clean_tasks.ts @@ -70,6 +70,8 @@ export const CleanExtraFilesFromModules: Task = { '**/Changelog.md', '**/changelog.md', + '**/CODE_OF_CONDUCT.md', + // examples '**/example', '**/examples', @@ -150,6 +152,9 @@ export const CleanExtraFilesFromModules: Task = { '**/.codecov.yml', '**/.airtap.yml', '**/.gitpod.yml', + '**/karma.conf.ci.js', + '**/karma.conf.js', + '**/karma-ci.conf.js', // metadata '**/package-lock.json', @@ -178,7 +183,25 @@ export const CleanExtraFilesFromModules: Task = { '**/*.tgz', '**/*.gz', + '**/*.cc', + '**/*.pl', + '**/*.py', + '**/*.gz', + '**/*.h', '**/*.xml', + '**/*.html', + + '**/*.development.js', + '**/*.dev.js', + '**/benchmark', + '**/benchmarks', + '**/benchmark.js', + '**/benchmarks.js', + + '**/rollup.config.js', + '**/webpack.config.js', + '**/commitlint.config.js', + '**/styleguide.config.js', '**/@elastic/eui/es', '**/@elastic/eui/test-env', diff --git a/src/dev/build/tasks/copy_source_task.ts b/src/dev/build/tasks/copy_source_task.ts index 0e4407cf767b0..27426f25b90f8 100644 --- a/src/dev/build/tasks/copy_source_task.ts +++ b/src/dev/build/tasks/copy_source_task.ts @@ -40,10 +40,15 @@ export const CopySource: Task = { '!**/jest*', '!**/*.{story,stories}.{js,ts}', - '!**/test_mocks.ts', + '!**/{test_mocks,stubs}.ts', '!**/*.{scss,console,d.ts,sh,md,mdx,asciidoc,docnav.json}', '!**/*.{test,test.mocks,mock,mocks,spec}.*', '!**/{packages,dev_docs,docs,public,__stories__,storybook,.storybook,ftr_e2e,e2e,scripts,test,tests,test_resources,test_data,__tests__,manual_tests,__jest__,__snapshots__,__mocks__,mock_responses,mocks,fixtures,__fixtures__,cypress,integration_tests}/**', + + '!x-pack/plugins/lens/to_playground.gif', // README.md + '!x-pack/plugins/lens/layout.png', // README.md + '!x-pack/plugins/cases/images', // README.md + '!x-pack/plugins/canvas/images', // unused ]; const piscina = new Piscina({ diff --git a/src/dev/build/tasks/nodejs/clean_node_builds_task.ts b/src/dev/build/tasks/nodejs/clean_node_builds_task.ts index 350395f94c355..bfa34d68f2a06 100644 --- a/src/dev/build/tasks/nodejs/clean_node_builds_task.ts +++ b/src/dev/build/tasks/nodejs/clean_node_builds_task.ts @@ -19,6 +19,8 @@ export const CleanNodeBuilds: Task = { build.resolvePathForPlatform(platform, 'node/bin/npm'), build.resolvePathForPlatform(platform, 'node/bin/npx'), build.resolvePathForPlatform(platform, 'node/bin/corepack'), + build.resolvePathForPlatform(platform, 'node/CHANGELOG.md'), + build.resolvePathForPlatform(platform, 'node/README.md'), ], log ); diff --git a/src/dev/license_checker/config.ts b/src/dev/license_checker/config.ts index 02649c2fc5b88..fea4d101c64ea 100644 --- a/src/dev/license_checker/config.ts +++ b/src/dev/license_checker/config.ts @@ -67,6 +67,7 @@ export const LICENSE_ALLOWED = [ 'WTFPL', 'Nuclide software', 'Python-2.0', + '(Apache-2.0 AND MIT)', ]; // The following list only applies to licenses that diff --git a/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.ts b/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.ts index d01a2400e038a..10067f702d906 100644 --- a/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.ts +++ b/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.ts @@ -102,6 +102,7 @@ export const metricVisFunction = (): MetricVisExpressionFunctionDefinition => ({ fn(input, args, handlers) { validateAccessor(args.metric, input.columns); validateAccessor(args.secondaryMetric, input.columns); + validateAccessor(args.max, input.columns); validateAccessor(args.breakdownBy, input.columns); if (handlers?.inspectorAdapters?.tables) { diff --git a/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.test.tsx b/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.test.tsx index fd0fbac29ae91..c94a0688588a1 100644 --- a/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.test.tsx +++ b/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.test.tsx @@ -1216,5 +1216,16 @@ describe('MetricVisComponent', function () { }, }); }); + + it('ignores suffix formatting', () => { + const { primary, secondary } = getFormattedMetrics(0.23939, 11.2, { + id: 'suffix', + params: { + id: 'percent', + }, + }); + expect(primary).toBe('23.94%'); + expect(secondary).toBe('1.12K%'); + }); }); }); diff --git a/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.tsx b/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.tsx index ef091c5245901..05a0266e676d0 100644 --- a/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.tsx +++ b/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.tsx @@ -80,7 +80,10 @@ const getMetricFormatter = ( columns: Datatable['columns'] ) => { const serializedFieldFormat = getFormatByAccessor(accessor, columns); - const formatId = serializedFieldFormat?.id ?? 'number'; + const formatId = + (serializedFieldFormat?.id === 'suffix' + ? serializedFieldFormat.params?.id + : serializedFieldFormat?.id) ?? 'number'; if ( !['number', 'currency', 'percent', 'bytes', 'duration', 'string', 'null'].includes(formatId) @@ -160,16 +163,11 @@ const getColor = ( data: Datatable, rowNumber: number ) => { - let minBound = paletteParams.rangeMin; - let maxBound = paletteParams.rangeMax; - const { min, max } = getDataBoundsForPalette(accessors, data, rowNumber); - minBound = min; - maxBound = max; return getPaletteService().get(CUSTOM_PALETTE)?.getColorForValue?.(value, paletteParams, { - min: minBound, - max: maxBound, + min, + max, }); }; diff --git a/src/plugins/controls/common/index.ts b/src/plugins/controls/common/index.ts index b8d7699e8061a..c3361c29232bd 100644 --- a/src/plugins/controls/common/index.ts +++ b/src/plugins/controls/common/index.ts @@ -27,6 +27,7 @@ export { persistableControlGroupInputIsEqual, getDefaultControlGroupInput, } from './control_group/control_group_persistence'; + export { DEFAULT_CONTROL_WIDTH, DEFAULT_CONTROL_STYLE, @@ -34,8 +35,6 @@ export { // Control Type exports export { OPTIONS_LIST_CONTROL, type OptionsListEmbeddableInput } from './options_list/types'; -export { - type RangeSliderEmbeddableInput, - RANGE_SLIDER_CONTROL, -} from './control_types/range_slider/types'; +export { type RangeSliderEmbeddableInput, RANGE_SLIDER_CONTROL } from './range_slider/types'; + export { TIME_SLIDER_CONTROL } from './control_types/time_slider/types'; diff --git a/src/plugins/controls/common/control_types/range_slider/range_slider_persistable_state.ts b/src/plugins/controls/common/range_slider/range_slider_persistable_state.ts similarity index 89% rename from src/plugins/controls/common/control_types/range_slider/range_slider_persistable_state.ts rename to src/plugins/controls/common/range_slider/range_slider_persistable_state.ts index bb4c703ba47be..d0dc0bd135cee 100644 --- a/src/plugins/controls/common/control_types/range_slider/range_slider_persistable_state.ts +++ b/src/plugins/controls/common/range_slider/range_slider_persistable_state.ts @@ -12,10 +12,9 @@ import { } from '@kbn/embeddable-plugin/common'; import { SavedObjectReference } from '@kbn/core/types'; import { DATA_VIEW_SAVED_OBJECT_TYPE } from '@kbn/data-views-plugin/common'; -import { RangeSliderEmbeddableInput } from './types'; +import { RangeSliderInputWithType } from './types'; -type RangeSliderInputWithType = Partial & { type: string }; -const dataViewReferenceName = 'optionsListDataView'; +const dataViewReferenceName = 'rangeSliderDataView'; export const createRangeSliderInject = (): EmbeddablePersistableStateService['inject'] => { return (state: EmbeddableStateWithType, references: SavedObjectReference[]) => { diff --git a/src/plugins/controls/common/control_types/range_slider/types.ts b/src/plugins/controls/common/range_slider/types.ts similarity index 79% rename from src/plugins/controls/common/control_types/range_slider/types.ts rename to src/plugins/controls/common/range_slider/types.ts index a975fdd27ac31..51c6d9e07e241 100644 --- a/src/plugins/controls/common/control_types/range_slider/types.ts +++ b/src/plugins/controls/common/range_slider/types.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { DataControlInput } from '../../types'; +import { DataControlInput } from '../types'; export const RANGE_SLIDER_CONTROL = 'rangeSliderControl'; @@ -15,3 +15,5 @@ export type RangeValue = [string, string]; export interface RangeSliderEmbeddableInput extends DataControlInput { value: RangeValue; } + +export type RangeSliderInputWithType = Partial & { type: string }; diff --git a/src/plugins/controls/public/__stories__/storybook_control_factories.ts b/src/plugins/controls/public/__stories__/storybook_control_factories.ts index 76968f104c275..9b0b41f6393e7 100644 --- a/src/plugins/controls/public/__stories__/storybook_control_factories.ts +++ b/src/plugins/controls/public/__stories__/storybook_control_factories.ts @@ -7,7 +7,7 @@ */ import { OptionsListEmbeddableFactory } from '../options_list'; -import { RangeSliderEmbeddableFactory } from '../control_types/range_slider'; +import { RangeSliderEmbeddableFactory } from '../range_slider'; import { TimesliderEmbeddableFactory } from '../control_types/time_slider'; import { ControlsService } from '../services/controls'; import { ControlFactory } from '..'; diff --git a/src/plugins/controls/public/control_types/range_slider/range_slider_strings.ts b/src/plugins/controls/public/control_types/range_slider/range_slider_strings.ts deleted file mode 100644 index 53d614fd54a2e..0000000000000 --- a/src/plugins/controls/public/control_types/range_slider/range_slider_strings.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 { i18n } from '@kbn/i18n'; - -export const RangeSliderStrings = { - getDisplayName: () => - i18n.translate('controls.rangeSlider.displayName', { - defaultMessage: 'Range slider', - }), - getDescription: () => - i18n.translate('controls.rangeSlider.description', { - defaultMessage: 'Add a control for selecting a range of field values.', - }), - editor: { - getDataViewTitle: () => - i18n.translate('controls.rangeSlider.editor.dataViewTitle', { - defaultMessage: 'Data view', - }), - getNoDataViewTitle: () => - i18n.translate('controls.rangeSlider.editor.noDataViewTitle', { - defaultMessage: 'Select data view', - }), - getFieldTitle: () => - i18n.translate('controls.rangeSlider.editor.fieldTitle', { - defaultMessage: 'Field', - }), - }, - popover: { - getAllOptionsButtonTitle: () => - i18n.translate('controls.rangeSlider.popover.allOptionsTitle', { - defaultMessage: 'Show all options', - }), - getClearRangeButtonTitle: () => - i18n.translate('controls.rangeSlider.popover.clearRangeTitle', { - defaultMessage: 'Clear range', - }), - getNoDataHelpText: () => - i18n.translate('controls.rangeSlider.popover.noDataHelpText', { - defaultMessage: 'Selected range resulted in no data. No filter was applied.', - }), - getNoAvailableDataHelpText: () => - i18n.translate('controls.rangeSlider.popover.noAvailableDataHelpText', { - defaultMessage: 'There is no data to display. Adjust the time range and filters.', - }), - }, - errors: { - getDataViewNotFoundError: (dataViewId: string) => - i18n.translate('controls.optionsList.errors.dataViewNotFound', { - defaultMessage: 'Could not locate data view: {dataViewId}', - values: { dataViewId }, - }), - getUpperLessThanLowerErrorMessage: () => - i18n.translate('controls.rangeSlider.popover.upperLessThanLowerErrorMessage', { - defaultMessage: 'The upper bound must be greater than or equal to the lower bound.', - }), - }, -}; diff --git a/src/plugins/controls/public/index.ts b/src/plugins/controls/public/index.ts index a3c54a73b07c1..f55df5fa0f53a 100644 --- a/src/plugins/controls/public/index.ts +++ b/src/plugins/controls/public/index.ts @@ -48,7 +48,7 @@ export { RangeSliderEmbeddableFactory, RangeSliderEmbeddable, type RangeSliderEmbeddableInput, -} from './control_types'; +} from './range_slider'; export { LazyControlsCallout, type CalloutProps } from './controls_callout'; diff --git a/src/plugins/controls/public/plugin.ts b/src/plugins/controls/public/plugin.ts index 13fc4d977c9f6..da45ba2e68684 100644 --- a/src/plugins/controls/public/plugin.ts +++ b/src/plugins/controls/public/plugin.ts @@ -8,7 +8,18 @@ import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; import { EmbeddableFactory } from '@kbn/embeddable-plugin/public'; + +import { + ControlGroupContainerFactory, + CONTROL_GROUP_TYPE, + OPTIONS_LIST_CONTROL, + RANGE_SLIDER_CONTROL, + // TIME_SLIDER_CONTROL, +} from '.'; +import { OptionsListEmbeddableFactory, OptionsListEmbeddableInput } from './options_list'; +import { RangeSliderEmbeddableFactory, RangeSliderEmbeddableInput } from './range_slider'; import { pluginServices } from './services'; +import { controlsService } from './services/kibana/controls'; import { ControlsPluginSetup, ControlsPluginStart, @@ -17,25 +28,13 @@ import { IEditableControlFactory, ControlInput, } from './types'; -import { OptionsListEmbeddableFactory, OptionsListEmbeddableInput } from './options_list'; -import { - RangeSliderEmbeddableFactory, - RangeSliderEmbeddableInput, -} from './control_types/range_slider'; -import { - ControlGroupContainerFactory, - CONTROL_GROUP_TYPE, - OPTIONS_LIST_CONTROL, - RANGE_SLIDER_CONTROL, - // TIME_SLIDER_CONTROL, -} from '.'; + /* import { TimesliderEmbeddableFactory, TimeSliderControlEmbeddableInput, } from './control_types/time_slider'; */ -import { controlsService } from './services/kibana/controls'; export class ControlsPlugin implements diff --git a/src/plugins/controls/public/control_types/range_slider/range_slider.scss b/src/plugins/controls/public/range_slider/components/range_slider.scss similarity index 100% rename from src/plugins/controls/public/control_types/range_slider/range_slider.scss rename to src/plugins/controls/public/range_slider/components/range_slider.scss diff --git a/src/plugins/controls/public/range_slider/components/range_slider_control.tsx b/src/plugins/controls/public/range_slider/components/range_slider_control.tsx new file mode 100644 index 0000000000000..8a88ba65609e4 --- /dev/null +++ b/src/plugins/controls/public/range_slider/components/range_slider_control.tsx @@ -0,0 +1,155 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { FC, useState, useRef } from 'react'; + +import { + EuiFieldNumber, + EuiText, + EuiInputPopover, + EuiLoadingSpinner, + EuiFlexGroup, + EuiFlexItem, + EuiDualRange, +} from '@elastic/eui'; +import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public'; + +import { rangeSliderReducers } from '../range_slider_reducers'; +import { RangeSliderReduxState } from '../types'; +import { RangeSliderPopover } from './range_slider_popover'; + +import './range_slider.scss'; + +const INVALID_CLASS = 'rangeSliderAnchor__fieldNumber--invalid'; + +export const RangeSliderControl: FC = () => { + const rangeRef = useRef(null); + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + // Controls Services Context + const { + useEmbeddableDispatch, + useEmbeddableSelector: select, + actions: { setSelectedRange }, + } = useReduxEmbeddableContext(); + const dispatch = useEmbeddableDispatch(); + + // Select current state from Redux using multiple selectors to avoid rerenders. + const min = select((state) => state.componentState.min); + const max = select((state) => state.componentState.max); + const isInvalid = select((state) => state.componentState.isInvalid); + const id = select((state) => state.explicitInput.id); + const value = select((state) => state.explicitInput.value) ?? ['', '']; + const isLoading = select((state) => state.output.loading); + + const hasAvailableRange = min !== '' && max !== ''; + + const hasLowerBoundSelection = value[0] !== ''; + const hasUpperBoundSelection = value[1] !== ''; + + const lowerBoundValue = parseFloat(value[0]); + const upperBoundValue = parseFloat(value[1]); + const minValue = parseFloat(min); + const maxValue = parseFloat(max); + + // EuiDualRange can only handle integers as min/max + const roundedMin = hasAvailableRange ? Math.floor(minValue) : minValue; + const roundedMax = hasAvailableRange ? Math.ceil(maxValue) : maxValue; + + const button = ( + + ); + + return ( + setIsPopoverOpen(false)} + anchorPosition="downCenter" + attachToAnchor={false} + disableFocusTrap + onPanelResize={() => { + if (rangeRef?.current) { + rangeRef.current.onResize(); + } + }} + > + + + ); +}; diff --git a/src/plugins/controls/public/control_types/range_slider/range_slider_popover.tsx b/src/plugins/controls/public/range_slider/components/range_slider_popover.tsx similarity index 52% rename from src/plugins/controls/public/control_types/range_slider/range_slider_popover.tsx rename to src/plugins/controls/public/range_slider/components/range_slider_popover.tsx index 4e674e752edaa..c14467fee33c8 100644 --- a/src/plugins/controls/public/control_types/range_slider/range_slider_popover.tsx +++ b/src/plugins/controls/public/range_slider/components/range_slider_popover.tsx @@ -6,90 +6,56 @@ * Side Public License, v 1. */ +import React, { FC, useEffect, useRef, useState } from 'react'; +import useMount from 'react-use/lib/useMount'; + import { - EuiFieldNumber, EuiPopoverTitle, - EuiText, - EuiInputPopover, - EuiButtonIcon, - EuiToolTip, - EuiLoadingSpinner, EuiFlexGroup, EuiFlexItem, EuiDualRange, + EuiText, + EuiToolTip, + EuiButtonIcon, } from '@elastic/eui'; -import React, { useState, useRef, useEffect } from 'react'; import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public'; -import { RangeSliderStrings } from './range_slider_strings'; -import { RangeSliderReduxState, RangeValue } from './types'; -import { rangeSliderReducers } from './range_slider_reducers'; import { pluginServices } from '../../services'; +import { rangeSliderReducers } from '../range_slider_reducers'; +import { RangeSliderReduxState, RangeValue } from '../types'; +import { RangeSliderStrings } from './range_slider_strings'; -const INVALID_CLASS = 'rangeSliderAnchor__fieldNumber--invalid'; +export const RangeSliderPopover: FC = () => { + const [fieldFormatter, setFieldFormatter] = useState(() => (toFormat: string) => toFormat); + const rangeRef = useRef(null); -export const RangeSliderPopover = () => { // Controls Services Context const { dataViews } = pluginServices.getHooks(); const { get: getDataViewById } = dataViews.useService(); - - const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const [rangeSliderMin, setRangeSliderMin] = useState(-Infinity); - const [rangeSliderMax, setRangeSliderMax] = useState(Infinity); - const [fieldFormatter, setFieldFormatter] = useState(() => (toFormat: string) => toFormat); - - const rangeRef = useRef(null); - const { useEmbeddableDispatch, useEmbeddableSelector: select, actions: { setSelectedRange }, } = useReduxEmbeddableContext(); + const dispatch = useEmbeddableDispatch(); // Select current state from Redux using multiple selectors to avoid rerenders. - const min = select((state) => state.componentState.min); - const max = select((state) => state.componentState.max); - const isInvalid = select((state) => state.componentState.isInvalid); + const dataViewId = select((state) => state.output.dataViewId); const fieldSpec = select((state) => state.componentState.field); - const id = select((state) => state.explicitInput.id); - const value = select((state) => state.explicitInput.value) ?? ['', '']; + const isInvalid = select((state) => state.componentState.isInvalid); + const max = select((state) => state.componentState.max); + const min = select((state) => state.componentState.min); const title = select((state) => state.explicitInput.title); - - const isLoading = select((state) => state.output.loading); - const dataViewId = select((state) => state.output.dataViewId); - - // derive field formatter from fieldSpec and dataViewId - useEffect(() => { - (async () => { - if (!dataViewId || !fieldSpec) return; - // dataViews are cached, and should always be available without having to hit ES. - const dataView = await getDataViewById(dataViewId); - setFieldFormatter( - () => - dataView?.getFormatterForField(fieldSpec).getConverterFor('text') ?? - ((toFormat: string) => toFormat) - ); - })(); - }, [fieldSpec, dataViewId, getDataViewById]); - - let errorMessage = ''; - let helpText = ''; + const value = select((state) => state.explicitInput.value) ?? ['', '']; const hasAvailableRange = min !== '' && max !== ''; - - if (!hasAvailableRange) { - helpText = RangeSliderStrings.popover.getNoAvailableDataHelpText(); - } else if (isInvalid) { - helpText = RangeSliderStrings.popover.getNoDataHelpText(); - } - const hasLowerBoundSelection = value[0] !== ''; const hasUpperBoundSelection = value[1] !== ''; - const lowerBoundValue = parseFloat(value[0]); - const upperBoundValue = parseFloat(value[1]); + const lowerBoundSelection = parseFloat(value[0]); + const upperBoundSelection = parseFloat(value[1]); const minValue = parseFloat(min); const maxValue = parseFloat(max); @@ -97,132 +63,74 @@ export const RangeSliderPopover = () => { const roundedMin = hasAvailableRange ? Math.floor(minValue) : minValue; const roundedMax = hasAvailableRange ? Math.ceil(maxValue) : maxValue; - if (lowerBoundValue > upperBoundValue) { - errorMessage = RangeSliderStrings.errors.getUpperLessThanLowerErrorMessage(); + // Caches min and max displayed on popover open so the range slider doesn't resize as selections change + const [rangeSliderMin, setRangeSliderMin] = useState(roundedMin); + const [rangeSliderMax, setRangeSliderMax] = useState(roundedMax); + + useMount(() => { + setRangeSliderMin( + Math.min( + roundedMin, + isNaN(lowerBoundSelection) ? Infinity : lowerBoundSelection, + isNaN(upperBoundSelection) ? Infinity : upperBoundSelection + ) + ); + setRangeSliderMax( + Math.max( + roundedMax, + isNaN(lowerBoundSelection) ? -Infinity : lowerBoundSelection, + isNaN(upperBoundSelection) ? -Infinity : upperBoundSelection + ) + ); + }); + + const errorMessage = ''; + let helpText = ''; + + if (!hasAvailableRange) { + helpText = RangeSliderStrings.popover.getNoAvailableDataHelpText(); + } else if (isInvalid) { + helpText = RangeSliderStrings.popover.getNoDataHelpText(); } const displayedValue = [ - hasLowerBoundSelection ? String(lowerBoundValue) : hasAvailableRange ? String(roundedMin) : '', - hasUpperBoundSelection ? String(upperBoundValue) : hasAvailableRange ? String(roundedMax) : '', + hasLowerBoundSelection + ? String(lowerBoundSelection) + : hasAvailableRange + ? String(roundedMin) + : '', + hasUpperBoundSelection + ? String(upperBoundSelection) + : hasAvailableRange + ? String(roundedMax) + : '', ] as RangeValue; const ticks = []; const levels = []; - if (hasAvailableRange && isPopoverOpen) { + if (hasAvailableRange) { ticks.push({ value: rangeSliderMin, label: fieldFormatter(String(rangeSliderMin)) }); ticks.push({ value: rangeSliderMax, label: fieldFormatter(String(rangeSliderMax)) }); levels.push({ min: roundedMin, max: roundedMax, color: 'success' }); } - const button = ( - - ); + // derive field formatter from fieldSpec and dataViewId + useEffect(() => { + (async () => { + if (!dataViewId || !fieldSpec) return; + // dataViews are cached, and should always be available without having to hit ES. + const dataView = await getDataViewById(dataViewId); + setFieldFormatter( + () => + dataView?.getFormatterForField(fieldSpec).getConverterFor('text') ?? + ((toFormat: string) => toFormat) + ); + })(); + }, [fieldSpec, dataViewId, getDataViewById]); return ( - setIsPopoverOpen(false)} - anchorPosition="downCenter" - attachToAnchor={false} - disableFocusTrap - onPanelResize={() => { - if (rangeRef?.current) { - rangeRef.current.onResize(); - } - }} - > + <> {title} { dispatch(setSelectedRange(['', '']))} + onClick={() => { + dispatch(setSelectedRange(['', ''])); + }} aria-label={RangeSliderStrings.popover.getClearRangeButtonTitle()} data-test-subj="rangeSlider__clearRangeButton" /> - + ); }; diff --git a/src/plugins/controls/public/range_slider/components/range_slider_strings.ts b/src/plugins/controls/public/range_slider/components/range_slider_strings.ts new file mode 100644 index 0000000000000..c016bc386b05a --- /dev/null +++ b/src/plugins/controls/public/range_slider/components/range_slider_strings.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; + +export const RangeSliderStrings = { + popover: { + getClearRangeButtonTitle: () => + i18n.translate('controls.rangeSlider.popover.clearRangeTitle', { + defaultMessage: 'Clear range', + }), + getNoDataHelpText: () => + i18n.translate('controls.rangeSlider.popover.noDataHelpText', { + defaultMessage: 'Selected range resulted in no data. No filter was applied.', + }), + getNoAvailableDataHelpText: () => + i18n.translate('controls.rangeSlider.popover.noAvailableDataHelpText', { + defaultMessage: 'There is no data to display. Adjust the time range and filters.', + }), + }, +}; diff --git a/src/plugins/controls/public/control_types/range_slider/range_slider_embeddable.tsx b/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx similarity index 93% rename from src/plugins/controls/public/control_types/range_slider/range_slider_embeddable.tsx rename to src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx index 699c0c42ceae7..bface6a8c40ae 100644 --- a/src/plugins/controls/public/control_types/range_slider/range_slider_embeddable.tsx +++ b/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx @@ -6,14 +6,6 @@ * Side Public License, v 1. */ -import { - compareFilters, - buildRangeFilter, - COMPARE_ALL_OPTIONS, - RangeFilterParams, - Filter, - Query, -} from '@kbn/es-query'; import React from 'react'; import ReactDOM from 'react-dom'; import { isEmpty } from 'lodash'; @@ -23,20 +15,27 @@ import deepEqual from 'fast-deep-equal'; import { Subscription, lastValueFrom } from 'rxjs'; import { debounceTime, distinctUntilChanged, skip, map } from 'rxjs/operators'; -import { ReduxEmbeddableTools, ReduxEmbeddablePackage } from '@kbn/presentation-util-plugin/public'; -import { Embeddable, IContainer } from '@kbn/embeddable-plugin/public'; import { DataView, DataViewField } from '@kbn/data-views-plugin/public'; +import { Embeddable, IContainer } from '@kbn/embeddable-plugin/public'; +import { + compareFilters, + buildRangeFilter, + COMPARE_ALL_OPTIONS, + RangeFilterParams, + Filter, + Query, +} from '@kbn/es-query'; +import { i18n } from '@kbn/i18n'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import { ReduxEmbeddableTools, ReduxEmbeddablePackage } from '@kbn/presentation-util-plugin/public'; -import { pluginServices } from '../../services'; import { ControlInput, ControlOutput } from '../..'; +import { pluginServices } from '../../services'; import { ControlsDataService } from '../../services/data'; import { ControlsDataViewsService } from '../../services/data_views'; - -import { RangeSliderStrings } from './range_slider_strings'; -import { RangeSliderComponent } from './range_slider.component'; -import { getDefaultComponentState, rangeSliderReducers } from './range_slider_reducers'; -import { RangeSliderEmbeddableInput, RangeSliderReduxState, RANGE_SLIDER_CONTROL } from './types'; +import { RangeSliderControl } from '../components/range_slider_control'; +import { getDefaultComponentState, rangeSliderReducers } from '../range_slider_reducers'; +import { RangeSliderEmbeddableInput, RangeSliderReduxState, RANGE_SLIDER_CONTROL } from '../types'; const diffDataFetchProps = ( current?: RangeSliderDataFetchProps, @@ -164,8 +163,16 @@ export class RangeSliderEmbeddable extends Embeddable - + , diff --git a/src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx b/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx similarity index 84% rename from src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx rename to src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx index 8610624a45daf..a0cf10f959390 100644 --- a/src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx +++ b/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable_factory.tsx @@ -10,26 +10,40 @@ import deepEqual from 'fast-deep-equal'; import { EmbeddableFactoryDefinition, IContainer } from '@kbn/embeddable-plugin/public'; import { lazyLoadReduxEmbeddablePackage } from '@kbn/presentation-util-plugin/public'; +import { i18n } from '@kbn/i18n'; -import { ControlEmbeddable, DataControlField, IEditableControlFactory } from '../../types'; -import { RangeSliderEmbeddableInput, RANGE_SLIDER_CONTROL } from './types'; import { createRangeSliderExtract, createRangeSliderInject, -} from '../../../common/control_types/range_slider/range_slider_persistable_state'; -import { RangeSliderStrings } from './range_slider_strings'; +} from '../../../common/range_slider/range_slider_persistable_state'; +import { ControlEmbeddable, DataControlField, IEditableControlFactory } from '../../types'; +import { RangeSliderEmbeddableInput, RANGE_SLIDER_CONTROL } from '../types'; export class RangeSliderEmbeddableFactory implements EmbeddableFactoryDefinition, IEditableControlFactory { public type = RANGE_SLIDER_CONTROL; + + public getDisplayName = () => + i18n.translate('controls.rangeSlider.displayName', { + defaultMessage: 'Range slider', + }); + + public getDescription = () => + i18n.translate('controls.rangeSlider.description', { + defaultMessage: 'Add a control for selecting a range of field values.', + }); + + public getIconType = () => 'controlsHorizontal'; + public canCreateNew = () => false; - constructor() {} + public isEditable = () => Promise.resolve(false); public async create(initialInput: RangeSliderEmbeddableInput, parent?: IContainer) { const reduxEmbeddablePackage = await lazyLoadReduxEmbeddablePackage(); const { RangeSliderEmbeddable } = await import('./range_slider_embeddable'); + return Promise.resolve( new RangeSliderEmbeddable(reduxEmbeddablePackage, initialInput, {}, parent) ); @@ -47,6 +61,7 @@ export class RangeSliderEmbeddableFactory // if the field name or data view id has changed in this editing session, selected values are invalid, so reset them. newInput.value = ['', '']; } + return newInput; }; @@ -56,12 +71,6 @@ export class RangeSliderEmbeddableFactory } }; - public isEditable = () => Promise.resolve(false); - - public getDisplayName = () => RangeSliderStrings.getDisplayName(); - public getIconType = () => 'controlsHorizontal'; - public getDescription = () => RangeSliderStrings.getDescription(); - public inject = createRangeSliderInject(); public extract = createRangeSliderExtract(); } diff --git a/src/plugins/controls/public/range_slider/index.ts b/src/plugins/controls/public/range_slider/index.ts new file mode 100644 index 0000000000000..bac4f92caa4a2 --- /dev/null +++ b/src/plugins/controls/public/range_slider/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 type { RangeSliderEmbeddableInput } from '../../common/range_slider/types'; +export { RANGE_SLIDER_CONTROL } from '../../common/range_slider/types'; + +export type { RangeSliderEmbeddable } from './embeddable/range_slider_embeddable'; +export { RangeSliderEmbeddableFactory } from './embeddable/range_slider_embeddable_factory'; diff --git a/src/plugins/controls/public/control_types/range_slider/range_slider_reducers.ts b/src/plugins/controls/public/range_slider/range_slider_reducers.ts similarity index 99% rename from src/plugins/controls/public/control_types/range_slider/range_slider_reducers.ts rename to src/plugins/controls/public/range_slider/range_slider_reducers.ts index 5bcf55770998f..c62347e3958f0 100644 --- a/src/plugins/controls/public/control_types/range_slider/range_slider_reducers.ts +++ b/src/plugins/controls/public/range_slider/range_slider_reducers.ts @@ -6,10 +6,11 @@ * Side Public License, v 1. */ -import { Filter } from '@kbn/es-query'; -import { PayloadAction } from '@reduxjs/toolkit'; import { WritableDraft } from 'immer/dist/types/types-external'; +import { PayloadAction } from '@reduxjs/toolkit'; + import { FieldSpec } from '@kbn/data-views-plugin/common'; +import { Filter } from '@kbn/es-query'; import { RangeSliderReduxState, RangeValue } from './types'; diff --git a/src/plugins/controls/public/control_types/range_slider/types.ts b/src/plugins/controls/public/range_slider/types.ts similarity index 80% rename from src/plugins/controls/public/control_types/range_slider/types.ts rename to src/plugins/controls/public/range_slider/types.ts index 390d91de08b88..8b30a797885d4 100644 --- a/src/plugins/controls/public/control_types/range_slider/types.ts +++ b/src/plugins/controls/public/range_slider/types.ts @@ -9,8 +9,8 @@ import { FieldSpec } from '@kbn/data-views-plugin/common'; import type { ReduxEmbeddableState } from '@kbn/presentation-util-plugin/public'; -import { RangeSliderEmbeddableInput } from '../../../common/control_types/range_slider/types'; -import { ControlOutput } from '../../types'; +import { RangeSliderEmbeddableInput } from '../../common/range_slider/types'; +import { ControlOutput } from '../types'; // Component state is only used by public components. export interface RangeSliderComponentState { @@ -27,4 +27,4 @@ export type RangeSliderReduxState = ReduxEmbeddableState< RangeSliderComponentState >; -export * from '../../../common/control_types/range_slider/types'; +export * from '../../common/range_slider/types'; diff --git a/src/plugins/controls/server/plugin.ts b/src/plugins/controls/server/plugin.ts index 97e40306ae25a..00d9688815796 100644 --- a/src/plugins/controls/server/plugin.ts +++ b/src/plugins/controls/server/plugin.ts @@ -7,13 +7,13 @@ */ import { CoreSetup, Plugin } from '@kbn/core/server'; - -import { EmbeddableSetup } from '@kbn/embeddable-plugin/server'; import { PluginSetup as DataSetup } from '@kbn/data-plugin/server'; +import { EmbeddableSetup } from '@kbn/embeddable-plugin/server'; import { PluginSetup as UnifiedSearchSetup } from '@kbn/unified-search-plugin/server'; import { setupOptionsListSuggestionsRoute } from './options_list/options_list_suggestions_route'; import { controlGroupContainerPersistableStateServiceFactory } from './control_group/control_group_container_factory'; import { optionsListPersistableStateServiceFactory } from './options_list/options_list_embeddable_factory'; +import { rangeSliderPersistableStateServiceFactory } from './range_slider/range_slider_embeddable_factory'; // import { timeSliderPersistableStateServiceFactory } from './control_types/time_slider/time_slider_embeddable_factory'; interface SetupDeps { @@ -25,6 +25,7 @@ interface SetupDeps { export class ControlsPlugin implements Plugin { public setup(core: CoreSetup, { embeddable, unifiedSearch }: SetupDeps) { embeddable.registerEmbeddableFactory(optionsListPersistableStateServiceFactory()); + embeddable.registerEmbeddableFactory(rangeSliderPersistableStateServiceFactory()); // Temporary disabling Time Slider // embeddable.registerEmbeddableFactory(timeSliderPersistableStateServiceFactory()); diff --git a/src/plugins/controls/server/range_slider/range_slider_embeddable_factory.ts b/src/plugins/controls/server/range_slider/range_slider_embeddable_factory.ts new file mode 100644 index 0000000000000..3b0c9ab7d1d4b --- /dev/null +++ b/src/plugins/controls/server/range_slider/range_slider_embeddable_factory.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { EmbeddableRegistryDefinition } from '@kbn/embeddable-plugin/server'; + +import { RANGE_SLIDER_CONTROL } from '../../common'; +import { + createRangeSliderExtract, + createRangeSliderInject, +} from '../../common/range_slider/range_slider_persistable_state'; + +export const rangeSliderPersistableStateServiceFactory = (): EmbeddableRegistryDefinition => { + return { + id: RANGE_SLIDER_CONTROL, + extract: createRangeSliderExtract(), + inject: createRangeSliderInject(), + }; +}; diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index c26b5a6ad8114..d970287253e3f 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -111,6 +111,7 @@ import { toAbsoluteDates, boundsDescendingRaw, getResponseInspectorStats, + calcAutoIntervalLessThan, // tabify tabifyAggResponse, tabifyGetColumns, @@ -217,6 +218,7 @@ export const search = { termsAggFilter, toAbsoluteDates, boundsDescendingRaw, + calcAutoIntervalLessThan, }, getResponseInspectorStats, tabifyAggResponse, diff --git a/src/plugins/data_view_editor/public/components/form_fields/title_field.tsx b/src/plugins/data_view_editor/public/components/form_fields/title_field.tsx index 6c41700ead531..822de9506500a 100644 --- a/src/plugins/data_view_editor/public/components/form_fields/title_field.tsx +++ b/src/plugins/data_view_editor/public/components/form_fields/title_field.tsx @@ -182,7 +182,6 @@ export const TitleField = ({ isInvalid={isInvalid} value={field.value} onChange={(e: ChangeEvent) => { - e.persist(); let query = e.target.value; if (query.length === 1 && !appendedWildcard && canAppendWildcard(query)) { query += '*'; diff --git a/src/plugins/data_view_field_editor/public/components/field_format_editor/editors/url/url.test.tsx b/src/plugins/data_view_field_editor/public/components/field_format_editor/editors/url/url.test.tsx index 4390d488e420a..56dbcceb189d7 100644 --- a/src/plugins/data_view_field_editor/public/components/field_format_editor/editors/url/url.test.tsx +++ b/src/plugins/data_view_field_editor/public/components/field_format_editor/editors/url/url.test.tsx @@ -104,7 +104,7 @@ describe('UrlFormatEditor', () => { userEvent.selectOptions(getByLabelText('Type'), 'img'); expect(sampleImageUrlTemplate).toContain(MY_BASE_PATH); expect(sampleImageUrlTemplate).toMatchInlineSnapshot( - `"my-base-path/plugins/indexPatternManagement/assets/icons/{{value}}.png"` + `"my-base-path/plugins/dataViewFieldEditor/assets/icons/{{value}}.png"` ); }); }); diff --git a/src/plugins/data_view_field_editor/public/components/field_format_editor/editors/url/url.tsx b/src/plugins/data_view_field_editor/public/components/field_format_editor/editors/url/url.tsx index fc10a23d9e12d..f298af2c29a0c 100644 --- a/src/plugins/data_view_field_editor/public/components/field_format_editor/editors/url/url.tsx +++ b/src/plugins/data_view_field_editor/public/components/field_format_editor/editors/url/url.tsx @@ -56,7 +56,7 @@ export class UrlFormatEditor extends DefaultFormatEditor< static contextType = contextType; static formatId = formatId; private get sampleIconPath() { - const sampleIconPath = `/plugins/indexPatternManagement/assets/icons/{{value}}.png`; + const sampleIconPath = `/plugins/dataViewFieldEditor/assets/icons/{{value}}.png`; return this.context?.services.http ? this.context.services.http.basePath.prepend(sampleIconPath) : sampleIconPath; diff --git a/src/plugins/data_view_management/public/assets/icons/LICENSE.txt b/src/plugins/data_view_management/public/assets/icons/LICENSE.txt deleted file mode 100644 index 1a86627c4a6b8..0000000000000 --- a/src/plugins/data_view_management/public/assets/icons/LICENSE.txt +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Steven Skelton - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/plugins/data_view_management/public/assets/icons/cv.png b/src/plugins/data_view_management/public/assets/icons/cv.png deleted file mode 100644 index 8f2ff8432e6bd..0000000000000 Binary files a/src/plugins/data_view_management/public/assets/icons/cv.png and /dev/null differ diff --git a/src/plugins/data_view_management/public/assets/icons/de.png b/src/plugins/data_view_management/public/assets/icons/de.png deleted file mode 100644 index 78279117b4d10..0000000000000 Binary files a/src/plugins/data_view_management/public/assets/icons/de.png and /dev/null differ diff --git a/src/plugins/data_view_management/public/assets/icons/go.png b/src/plugins/data_view_management/public/assets/icons/go.png deleted file mode 100644 index 34c317db5adf3..0000000000000 Binary files a/src/plugins/data_view_management/public/assets/icons/go.png and /dev/null differ diff --git a/src/plugins/data_view_management/public/assets/icons/ne.png b/src/plugins/data_view_management/public/assets/icons/ne.png deleted file mode 100644 index d331209e17998..0000000000000 Binary files a/src/plugins/data_view_management/public/assets/icons/ne.png and /dev/null differ diff --git a/src/plugins/data_view_management/public/assets/icons/ni.png b/src/plugins/data_view_management/public/assets/icons/ni.png deleted file mode 100644 index e5bdb0b668d41..0000000000000 Binary files a/src/plugins/data_view_management/public/assets/icons/ni.png and /dev/null differ diff --git a/src/plugins/data_view_management/public/assets/icons/stop.png b/src/plugins/data_view_management/public/assets/icons/stop.png deleted file mode 100644 index 4bf65fc96f59f..0000000000000 Binary files a/src/plugins/data_view_management/public/assets/icons/stop.png and /dev/null differ diff --git a/src/plugins/data_view_management/public/assets/icons/us.png b/src/plugins/data_view_management/public/assets/icons/us.png deleted file mode 100644 index f30f21f85d06a..0000000000000 Binary files a/src/plugins/data_view_management/public/assets/icons/us.png and /dev/null differ diff --git a/src/plugins/discover/public/application/context/context_app_content.tsx b/src/plugins/discover/public/application/context/context_app_content.tsx index 3ba518c4a3df8..7471f15092c3b 100644 --- a/src/plugins/discover/public/application/context/context_app_content.tsx +++ b/src/plugins/discover/public/application/context/context_app_content.tsx @@ -11,6 +11,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { EuiHorizontalRule, EuiText } from '@elastic/eui'; import type { DataView } from '@kbn/data-views-plugin/public'; import { SortDirection } from '@kbn/data-plugin/public'; +import type { SortOrder } from '@kbn/saved-search-plugin/public'; import { CONTEXT_STEP_SETTING, DOC_HIDE_TIME_COLUMN_SETTING } from '../../../common'; import { LoadingStatus } from './services/context_query_state'; import { ActionBar } from './components/action_bar/action_bar'; @@ -20,7 +21,6 @@ import { AppState } from './services/context_state'; import { SurrDocType } from './services/context'; import { MAX_CONTEXT_SIZE, MIN_CONTEXT_SIZE } from './services/constants'; import { DocTableContext } from '../../components/doc_table/doc_table_context'; -import type { SortPairArr } from '../../components/doc_table/utils/get_sort'; import { useDiscoverServices } from '../../hooks/use_discover_services'; import type { DataTableRecord } from '../../types'; @@ -151,7 +151,7 @@ export function ContextAppContent({ expandedDoc={expandedDoc} isLoading={isAnchorLoading} sampleSize={0} - sort={sort as SortPairArr[]} + sort={sort as SortOrder[]} isSortEnabled={false} showTimeCol={showTimeCol} useNewFieldsApi={useNewFieldsApi} diff --git a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx index 92443cea9f1ae..dfff574659744 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx @@ -15,7 +15,7 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { DataView } from '@kbn/data-views-plugin/public'; -import { SavedSearch } from '@kbn/saved-search-plugin/public'; +import { SavedSearch, SortOrder } from '@kbn/saved-search-plugin/public'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { DocViewFilterFn } from '../../../../services/doc_views/doc_views_types'; import { DiscoverGrid } from '../../../../components/discover_grid/discover_grid'; @@ -32,7 +32,6 @@ import { DataDocuments$, DataDocumentsMsg, RecordRawType } from '../../hooks/use import { AppState, GetStateReturn } from '../../services/discover_state'; import { useDataState } from '../../hooks/use_data_state'; import { DocTableInfinite } from '../../../../components/doc_table/doc_table_infinite'; -import { SortPairArr } from '../../../../components/doc_table/utils/get_sort'; import { DocumentExplorerCallout } from '../document_explorer_callout'; import { DocumentExplorerUpdateCallout } from '../document_explorer_callout/document_explorer_update_callout'; import { DiscoverTourProvider } from '../../../../components/discover_tour'; @@ -195,7 +194,7 @@ function DiscoverDocumentsComponent({ dataView={dataView} isLoading={isLoading} rows={rows} - sort={(state.sort as SortPairArr[]) || []} + sort={(state.sort as SortOrder[]) || []} sampleSize={sampleSize} searchDescription={savedSearch.description} searchTitle={savedSearch.title} diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx index 6fbeca677bc63..743e0005ec756 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx @@ -397,6 +397,9 @@ function DiscoverFieldComponent({ const renderPopover = () => { const details = getDetails(field); + + // TODO: integrate + return ( <> {showFieldStats && ( diff --git a/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx b/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx index 9f0c1f9552f2c..e99295fee9e97 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx @@ -6,8 +6,10 @@ * Side Public License, v 1. */ -import React from 'react'; +import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; +import { EuiFormRow, EuiSwitch } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; import { SavedObjectSaveModal, showSaveModal, OnSaveProps } from '@kbn/saved-objects-plugin/public'; import { DataView } from '@kbn/data-views-plugin/public'; import { SavedSearch, SaveSavedSearchOptions } from '@kbn/saved-search-plugin/public'; @@ -108,20 +110,24 @@ export async function onSaveSearch({ const onSave = async ({ newTitle, newCopyOnSave, + newTimeRestore, newDescription, isTitleDuplicateConfirmed, onTitleDuplicate, }: { newTitle: string; + newTimeRestore: boolean; newCopyOnSave: boolean; newDescription: string; isTitleDuplicateConfirmed: boolean; onTitleDuplicate: () => void; }) => { const currentTitle = savedSearch.title; + const currentTimeRestore = savedSearch.timeRestore; const currentRowsPerPage = savedSearch.rowsPerPage; savedSearch.title = newTitle; savedSearch.description = newDescription; + savedSearch.timeRestore = newTimeRestore; savedSearch.rowsPerPage = uiSettings.get(DOC_TABLE_LEGACY) ? currentRowsPerPage : state.appStateContainer.getState().rowsPerPage; @@ -143,6 +149,7 @@ export async function onSaveSearch({ // If the save wasn't successful, put the original values back. if (!response.id || response.error) { savedSearch.title = currentTitle; + savedSearch.timeRestore = currentTimeRestore; savedSearch.rowsPerPage = currentRowsPerPage; } else { state.resetInitialAppState(); @@ -156,6 +163,7 @@ export async function onSaveSearch({ title={savedSearch.title ?? ''} showCopyOnSave={!!savedSearch.id} description={savedSearch.description} + timeRestore={savedSearch.timeRestore} onSave={onSave} onClose={onClose ?? (() => {})} /> @@ -167,13 +175,42 @@ const SaveSearchObjectModal: React.FC<{ title: string; showCopyOnSave: boolean; description?: string; - onSave: (props: OnSaveProps & { newRowsPerPage?: number }) => void; + timeRestore?: boolean; + onSave: (props: OnSaveProps & { newTimeRestore: boolean }) => void; onClose: () => void; -}> = ({ title, description, showCopyOnSave, onSave, onClose }) => { +}> = ({ title, description, showCopyOnSave, timeRestore: savedTimeRestore, onSave, onClose }) => { + const [timeRestore, setTimeRestore] = useState(savedTimeRestore || false); + const onModalSave = (params: OnSaveProps) => { - onSave(params); + onSave({ + ...params, + newTimeRestore: timeRestore, + }); }; + const options = ( + + } + > + setTimeRestore(event.target.checked)} + label={ + + } + /> + + ); + return ( diff --git a/src/plugins/discover/public/application/main/discover_main_route.test.tsx b/src/plugins/discover/public/application/main/discover_main_route.test.tsx index 6734864210ee6..aead62c038e47 100644 --- a/src/plugins/discover/public/application/main/discover_main_route.test.tsx +++ b/src/plugins/discover/public/application/main/discover_main_route.test.tsx @@ -22,7 +22,7 @@ import { searchSourceInstanceMock } from '@kbn/data-plugin/common/search/search_ import { findTestSubject } from '@elastic/eui/lib/test'; jest.mock('./discover_main_app', () => { return { - DiscoverMainApp: jest.fn(), + DiscoverMainApp: jest.fn().mockReturnValue(<>), }; }); diff --git a/src/plugins/discover/public/application/main/discover_main_route.tsx b/src/plugins/discover/public/application/main/discover_main_route.tsx index cec20f64d82b2..e8e1ec43bd42d 100644 --- a/src/plugins/discover/public/application/main/discover_main_route.tsx +++ b/src/plugins/discover/public/application/main/discover_main_route.tsx @@ -32,6 +32,7 @@ import { LoadingIndicator } from '../../components/common/loading_indicator'; import { DiscoverError } from '../../components/common/error_alert'; import { useDiscoverServices } from '../../hooks/use_discover_services'; import { getUrlTracker } from '../../kibana_services'; +import { restoreStateFromSavedSearch } from '../../services/saved_searches/restore_from_saved_search'; const DiscoverMainAppMemoized = memo(DiscoverMainApp); @@ -129,6 +130,11 @@ export function DiscoverMainRoute(props: Props) { currentSavedSearch.searchSource.setField('index', currentDataView); } + restoreStateFromSavedSearch({ + savedSearch: currentSavedSearch, + timefilter: services.timefilter, + }); + setSavedSearch(currentSavedSearch); if (currentSavedSearch.id) { @@ -163,8 +169,9 @@ export function DiscoverMainRoute(props: Props) { } }, [ id, - services.data.search, + services.data, services.spaces, + services.timefilter, core.savedObjects.client, core.application.navigateToApp, core.theme, diff --git a/src/plugins/discover/public/application/main/hooks/use_discover_state.ts b/src/plugins/discover/public/application/main/hooks/use_discover_state.ts index b5cf634ebe3a0..b3aaed0d880db 100644 --- a/src/plugins/discover/public/application/main/hooks/use_discover_state.ts +++ b/src/plugins/discover/public/application/main/hooks/use_discover_state.ts @@ -17,6 +17,7 @@ import { Query, } from '@kbn/es-query'; import { SavedSearch, getSavedSearch } from '@kbn/saved-search-plugin/public'; +import type { SortOrder } from '@kbn/saved-search-plugin/public'; import { getState } from '../services/discover_state'; import { getStateDefaults } from '../utils/get_state_defaults'; import { DiscoverServices } from '../../../build_services'; @@ -32,8 +33,8 @@ import { useSearchSession } from './use_search_session'; import { useDataState } from './use_data_state'; import { FetchStatus } from '../../types'; import { getDataViewAppState } from '../utils/get_switch_data_view_app_state'; -import { SortPairArr } from '../../../components/doc_table/utils/get_sort'; import { DataTableRecord } from '../../../types'; +import { restoreStateFromSavedSearch } from '../../../services/saved_searches/restore_from_saved_search'; const MAX_NUM_OF_COLUMNS = 50; @@ -193,6 +194,12 @@ export function useDiscoverState({ savedSearch: newSavedSearch, storage, }); + + restoreStateFromSavedSearch({ + savedSearch: newSavedSearch, + timefilter: services.timefilter, + }); + await stateContainer.replaceUrlAppState(newAppState); setState(newAppState); }, @@ -210,7 +217,7 @@ export function useDiscoverState({ dataView, nextDataView, state.columns || [], - (state.sort || []) as SortPairArr[], + (state.sort || []) as SortOrder[], config.get(MODIFY_COLUMNS_ON_SWITCH), config.get(SORT_DEFAULT_ORDER_SETTING), state.query diff --git a/src/plugins/discover/public/application/main/utils/get_state_defaults.ts b/src/plugins/discover/public/application/main/utils/get_state_defaults.ts index 5eb863372d90c..f058c473127d4 100644 --- a/src/plugins/discover/public/application/main/utils/get_state_defaults.ts +++ b/src/plugins/discover/public/application/main/utils/get_state_defaults.ts @@ -11,6 +11,7 @@ import { IUiSettingsClient } from '@kbn/core/public'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { Storage } from '@kbn/kibana-utils-plugin/public'; import { SavedSearch } from '@kbn/saved-search-plugin/public'; +import { getDefaultSort, getSortArray } from '../../../utils/sorting'; import { DEFAULT_COLUMNS_SETTING, DOC_HIDE_TIME_COLUMN_SETTING, @@ -19,7 +20,6 @@ import { } from '../../../../common'; import { AppState } from '../services/discover_state'; -import { getDefaultSort, getSortArray } from '../../../components/doc_table'; import { CHART_HIDDEN_KEY } from '../components/chart/discover_chart'; function getDefaultColumns(savedSearch: SavedSearch, config: IUiSettingsClient) { diff --git a/src/plugins/discover/public/application/main/utils/get_switch_data_view_app_state.ts b/src/plugins/discover/public/application/main/utils/get_switch_data_view_app_state.ts index 6ed489e504231..064fe36d974ec 100644 --- a/src/plugins/discover/public/application/main/utils/get_switch_data_view_app_state.ts +++ b/src/plugins/discover/public/application/main/utils/get_switch_data_view_app_state.ts @@ -7,7 +7,8 @@ */ import { isOfAggregateQueryType, Query, AggregateQuery } from '@kbn/es-query'; import type { DataView } from '@kbn/data-views-plugin/public'; -import { getSortArray, SortPairArr } from '../../../components/doc_table/utils/get_sort'; +import type { SortOrder } from '@kbn/saved-search-plugin/public'; +import { getSortArray } from '../../../utils/sorting'; /** * Helper function to remove or adapt the currently selected columns/sort to be valid with the next @@ -17,7 +18,7 @@ export function getDataViewAppState( currentDataView: DataView, nextDataView: DataView, currentColumns: string[], - currentSort: SortPairArr[], + currentSort: SortOrder[], modifyColumns: boolean = true, sortDirection: string = 'desc', query?: Query | AggregateQuery diff --git a/src/plugins/discover/public/application/main/utils/persist_saved_search.ts b/src/plugins/discover/public/application/main/utils/persist_saved_search.ts index 691a8a6604194..488f9598aaae7 100644 --- a/src/plugins/discover/public/application/main/utils/persist_saved_search.ts +++ b/src/plugins/discover/public/application/main/utils/persist_saved_search.ts @@ -8,9 +8,7 @@ import { isOfAggregateQueryType } from '@kbn/es-query'; import { DataView } from '@kbn/data-views-plugin/public'; import { SavedObjectSaveOpts } from '@kbn/saved-objects-plugin/public'; -import { SavedSearch } from '@kbn/saved-search-plugin/public'; -import type { SortOrder } from '@kbn/saved-search-plugin/public'; -import { saveSavedSearch } from '@kbn/saved-search-plugin/public'; +import { SavedSearch, SortOrder, saveSavedSearch } from '@kbn/saved-search-plugin/public'; import { updateSearchSource } from './update_search_source'; import { AppState } from '../services/discover_state'; import { DiscoverServices } from '../../../build_services'; @@ -69,6 +67,20 @@ export async function persistSavedSearch( savedSearch.isTextBasedQuery = isTextBasedQuery; } + const { from, to } = services.timefilter.getTime(); + const refreshInterval = services.timefilter.getRefreshInterval(); + savedSearch.timeRange = + savedSearch.timeRestore || savedSearch.timeRange + ? { + from, + to, + } + : undefined; + savedSearch.refreshInterval = + savedSearch.timeRestore || savedSearch.refreshInterval + ? { value: refreshInterval.value, pause: refreshInterval.pause } + : undefined; + try { const id = await saveSavedSearch(savedSearch, saveOptions, services.core.savedObjects.client); if (id) { diff --git a/src/plugins/discover/public/application/main/utils/update_search_source.ts b/src/plugins/discover/public/application/main/utils/update_search_source.ts index aea864af91d81..4966a66cf9687 100644 --- a/src/plugins/discover/public/application/main/utils/update_search_source.ts +++ b/src/plugins/discover/public/application/main/utils/update_search_source.ts @@ -11,7 +11,7 @@ import { DataViewType, DataView } from '@kbn/data-views-plugin/public'; import type { SortOrder } from '@kbn/saved-search-plugin/public'; import { SORT_DEFAULT_ORDER_SETTING } from '../../../../common'; import { DiscoverServices } from '../../../build_services'; -import { getSortForSearchSource } from '../../../components/doc_table'; +import { getSortForSearchSource } from '../../../utils/sorting'; /** * Helper function to update the given searchSource before fetching/sharing/persisting diff --git a/src/plugins/discover/public/application/main/utils/validate_time_range.ts b/src/plugins/discover/public/application/main/utils/validate_time_range.ts index 83d5f18de7f95..65748bbd75ce9 100644 --- a/src/plugins/discover/public/application/main/utils/validate_time_range.ts +++ b/src/plugins/discover/public/application/main/utils/validate_time_range.ts @@ -6,9 +6,9 @@ * Side Public License, v 1. */ -import dateMath from '@kbn/datemath'; import { i18n } from '@kbn/i18n'; import { ToastsStart } from '@kbn/core/public'; +import { isTimeRangeValid } from '../../../utils/validate_time'; /** * Validates a given time filter range, provided by URL or UI @@ -18,9 +18,7 @@ export function validateTimeRange( { from, to }: { from: string; to: string }, toastNotifications: ToastsStart ): boolean { - const fromMoment = dateMath.parse(from); - const toMoment = dateMath.parse(to); - if (!fromMoment || !toMoment || !fromMoment.isValid() || !toMoment.isValid()) { + if (!isTimeRangeValid({ from, to })) { toastNotifications.addDanger({ title: i18n.translate('discover.notifications.invalidTimeRangeTitle', { defaultMessage: `Invalid time range`, diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid.tsx b/src/plugins/discover/public/components/discover_grid/discover_grid.tsx index d02529d924e3f..1987542931676 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid.tsx +++ b/src/plugins/discover/public/components/discover_grid/discover_grid.tsx @@ -23,6 +23,7 @@ import { EuiLink, } from '@elastic/eui'; import type { DataView } from '@kbn/data-views-plugin/public'; +import type { SortOrder } from '@kbn/saved-search-plugin/public'; import { DocViewFilterFn } from '../../services/doc_views/doc_views_types'; import { getSchemaDetectors } from './discover_grid_schema'; import { DiscoverGridFlyout } from './discover_grid_flyout'; @@ -43,7 +44,6 @@ import { SHOW_MULTIFIELDS, } from '../../../common'; import { DiscoverGridDocumentToolbarBtn } from './discover_grid_document_selection'; -import { SortPairArr } from '../doc_table/utils/get_sort'; import { getFieldsToShow } from '../../utils/get_fields_to_show'; import type { DataTableRecord, ValueToStringConverter } from '../../types'; import { useRowHeightsOptions } from '../../hooks/use_row_heights_options'; @@ -141,7 +141,7 @@ export interface DiscoverGridProps { /** * Current sort setting */ - sort: SortPairArr[]; + sort: SortOrder[]; /** * How the data is fetched */ diff --git a/src/plugins/discover/public/components/doc_table/components/table_header/helpers.tsx b/src/plugins/discover/public/components/doc_table/components/table_header/helpers.tsx index 47b96d266067b..57cdc35bad016 100644 --- a/src/plugins/discover/public/components/doc_table/components/table_header/helpers.tsx +++ b/src/plugins/discover/public/components/doc_table/components/table_header/helpers.tsx @@ -9,7 +9,6 @@ import { i18n } from '@kbn/i18n'; import type { DataView } from '@kbn/data-views-plugin/public'; -export type SortOrder = [string, string]; export interface ColumnProps { name: string; displayName: string; diff --git a/src/plugins/discover/public/components/doc_table/components/table_header/table_header.test.tsx b/src/plugins/discover/public/components/doc_table/components/table_header/table_header.test.tsx index 2b1937c177e15..78d15870e6353 100644 --- a/src/plugins/discover/public/components/doc_table/components/table_header/table_header.test.tsx +++ b/src/plugins/discover/public/components/doc_table/components/table_header/table_header.test.tsx @@ -9,9 +9,9 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; +import type { SortOrder } from '@kbn/saved-search-plugin/public'; import { TableHeader } from './table_header'; import { findTestSubject } from '@elastic/eui/lib/test'; -import { SortOrder } from './helpers'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { DOC_HIDE_TIME_COLUMN_SETTING } from '../../../../../common'; import { FORMATS_UI_SETTINGS } from '@kbn/field-formats-plugin/common'; diff --git a/src/plugins/discover/public/components/doc_table/components/table_header/table_header.tsx b/src/plugins/discover/public/components/doc_table/components/table_header/table_header.tsx index ac9dc4c8e204f..ef5ca408d2bd8 100644 --- a/src/plugins/discover/public/components/doc_table/components/table_header/table_header.tsx +++ b/src/plugins/discover/public/components/doc_table/components/table_header/table_header.tsx @@ -9,9 +9,10 @@ import React, { useMemo } from 'react'; import type { DataView } from '@kbn/data-views-plugin/public'; import { FORMATS_UI_SETTINGS } from '@kbn/field-formats-plugin/common'; +import type { SortOrder } from '@kbn/saved-search-plugin/public'; import { TableHeaderColumn } from './table_header_column'; -import { SortOrder, getDisplayedColumns } from './helpers'; -import { getDefaultSort } from '../../utils/get_default_sort'; +import { getDisplayedColumns } from './helpers'; +import { getDefaultSort } from '../../../../utils/sorting'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { DOC_HIDE_TIME_COLUMN_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../../../common'; diff --git a/src/plugins/discover/public/components/doc_table/components/table_header/table_header_column.tsx b/src/plugins/discover/public/components/doc_table/components/table_header/table_header_column.tsx index 80a1aee5e920f..f7322b778a558 100644 --- a/src/plugins/discover/public/components/doc_table/components/table_header/table_header_column.tsx +++ b/src/plugins/discover/public/components/doc_table/components/table_header/table_header_column.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonIcon, EuiToolTip, EuiIconTip } from '@elastic/eui'; -import { SortOrder } from './helpers'; +import type { SortOrder } from '@kbn/saved-search-plugin/public'; import { DocViewTableScoreSortWarning } from './score_sort_warning'; interface Props { diff --git a/src/plugins/discover/public/components/doc_table/doc_table_wrapper.tsx b/src/plugins/discover/public/components/doc_table/doc_table_wrapper.tsx index c7036deaff50b..23f8b3e2e888b 100644 --- a/src/plugins/discover/public/components/doc_table/doc_table_wrapper.tsx +++ b/src/plugins/discover/public/components/doc_table/doc_table_wrapper.tsx @@ -9,10 +9,10 @@ import React, { forwardRef, useCallback, useMemo } from 'react'; import { EuiIcon, EuiSpacer, EuiText } from '@elastic/eui'; import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; +import type { SortOrder } from '@kbn/saved-search-plugin/public'; import { FormattedMessage } from '@kbn/i18n-react'; import { TableHeader } from './components/table_header/table_header'; import { SHOW_MULTIFIELDS } from '../../../common'; -import { SortOrder } from './components/table_header/helpers'; import { TableRow } from './components/table_row'; import { DocViewFilterFn } from '../../services/doc_views/doc_views_types'; import { getFieldsToShow } from '../../utils/get_fields_to_show'; diff --git a/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx b/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx index b4c3f41e4ba36..fc9afe946fae0 100644 --- a/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx +++ b/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx @@ -22,6 +22,7 @@ import { I18nProvider } from '@kbn/i18n-react'; import type { KibanaExecutionContext } from '@kbn/core/public'; import { Container, Embeddable, FilterableEmbeddable } from '@kbn/embeddable-plugin/public'; import { Adapters, RequestAdapter } from '@kbn/inspector-plugin/common'; +import type { SortOrder } from '@kbn/saved-search-plugin/public'; import { APPLY_FILTER_TRIGGER, FilterManager, @@ -33,6 +34,7 @@ import { DataView, DataViewField } from '@kbn/data-views-plugin/public'; import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { SavedSearch } from '@kbn/saved-search-plugin/public'; +import { getSortForEmbeddable, SortPair } from '../utils/sorting'; import { RecordRawType } from '../application/main/hooks/use_saved_search'; import { buildDataTableRecord } from '../utils/build_data_record'; import { DataTableRecord, EsHitRecord } from '../types'; @@ -53,8 +55,6 @@ import { handleSourceColumnState } from '../utils/state_helpers'; import { DiscoverGridProps } from '../components/discover_grid/discover_grid'; import { DiscoverGridSettings } from '../components/discover_grid/types'; import { DocTableProps } from '../components/doc_table/doc_table_wrapper'; -import { getDefaultSort } from '../components/doc_table'; -import { SortOrder } from '../components/doc_table/components/table_header/helpers'; import { VIEW_MODE } from '../components/view_mode_toggle'; import { updateSearchSource } from './utils/update_search_source'; import { FieldStatisticsTable } from '../application/main/components/field_stats_table'; @@ -103,6 +103,7 @@ export class SavedSearchEmbeddable private prevTimeRange?: TimeRange; private prevFilters?: Filter[]; private prevQuery?: Query; + private prevSort?: SortOrder[]; private prevSearchSessionId?: string; private searchProps?: SearchProps; @@ -285,10 +286,8 @@ export class SavedSearchEmbeddable } }; - private getDefaultSort(dataView?: DataView) { - const defaultSortOrder = this.services.uiSettings.get(SORT_DEFAULT_ORDER_SETTING, 'desc'); - const hidingTimeColumn = this.services.uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false); - return getDefaultSort(dataView, defaultSortOrder, hidingTimeColumn); + private getSort(sort: SortPair[] | undefined, dataView?: DataView) { + return getSortForEmbeddable(sort, dataView, this.services.uiSettings); } private initializeSearchEmbeddableProps() { @@ -299,16 +298,13 @@ export class SavedSearchEmbeddable if (!dataView) { return; } - - if (!this.savedSearch.sort || !this.savedSearch.sort.length) { - this.savedSearch.sort = this.getDefaultSort(dataView); - } + const sort = this.getSort(this.savedSearch.sort, dataView); const props: SearchProps = { columns: this.savedSearch.columns, dataView, isLoading: false, - sort: this.getDefaultSort(dataView), + sort, rows: [], searchDescription: this.savedSearch.description, description: this.savedSearch.description, @@ -339,9 +335,9 @@ export class SavedSearchEmbeddable onSetColumns: (columns: string[]) => { this.updateInput({ columns }); }, - onSort: (sort: string[][]) => { + onSort: (nextSort: string[][]) => { const sortOrderArr: SortOrder[] = []; - sort.forEach((arr) => { + nextSort.forEach((arr) => { sortOrderArr.push(arr as SortOrder); }); this.updateInput({ sort: sortOrderArr }); @@ -400,14 +396,15 @@ export class SavedSearchEmbeddable } private isFetchRequired(searchProps?: SearchProps) { - if (!searchProps) { + if (!searchProps || !searchProps.dataView) { return false; } + return ( !onlyDisabledFiltersChanged(this.input.filters, this.prevFilters) || !isEqual(this.prevQuery, this.input.query) || !isEqual(this.prevTimeRange, this.input.timeRange) || - !isEqual(searchProps.sort, this.input.sort || this.savedSearch.sort) || + !isEqual(this.prevSort, this.input.sort) || this.prevSearchSessionId !== this.input.searchSessionId ); } @@ -431,12 +428,11 @@ export class SavedSearchEmbeddable { columns: this.input.columns || this.savedSearch.columns }, this.services.core.uiSettings ).columns; + searchProps.sort = this.getSort( + this.input.sort || this.savedSearch.sort, + searchProps?.dataView + ); - const savedSearchSort = - this.savedSearch.sort && this.savedSearch.sort.length - ? this.savedSearch.sort - : this.getDefaultSort(this.searchProps?.dataView); - searchProps.sort = this.input.sort || savedSearchSort; searchProps.sharedItemTitle = this.panelTitle; searchProps.rowHeightState = this.input.rowHeight || this.savedSearch.rowHeight; searchProps.rowsPerPageState = this.input.rowsPerPage || this.savedSearch.rowsPerPage; @@ -453,6 +449,7 @@ export class SavedSearchEmbeddable this.prevQuery = this.input.query; this.prevTimeRange = this.input.timeRange; this.prevSearchSessionId = this.input.searchSessionId; + this.prevSort = this.input.sort; this.searchProps = searchProps; await this.fetch(); } else if (this.searchProps && this.node) { diff --git a/src/plugins/discover/public/embeddable/types.ts b/src/plugins/discover/public/embeddable/types.ts index dacb9b7d81d43..6038aa5a0625c 100644 --- a/src/plugins/discover/public/embeddable/types.ts +++ b/src/plugins/discover/public/embeddable/types.ts @@ -15,7 +15,7 @@ import { import type { Filter, TimeRange, Query } from '@kbn/es-query'; import { DataView } from '@kbn/data-views-plugin/public'; import { SavedSearch } from '@kbn/saved-search-plugin/public'; -import { SortOrder } from '../components/doc_table/components/table_header/helpers'; +import type { SortOrder } from '@kbn/saved-search-plugin/public'; export interface SearchInput extends EmbeddableInput { timeRange: TimeRange; diff --git a/src/plugins/discover/public/embeddable/utils/update_search_source.ts b/src/plugins/discover/public/embeddable/utils/update_search_source.ts index 0126ed4d021e6..87ee137aed796 100644 --- a/src/plugins/discover/public/embeddable/utils/update_search_source.ts +++ b/src/plugins/discover/public/embeddable/utils/update_search_source.ts @@ -6,14 +6,14 @@ * Side Public License, v 1. */ import type { DataView } from '@kbn/data-views-plugin/public'; -import { ISearchSource } from '@kbn/data-plugin/public'; -import { getSortForSearchSource } from '../../components/doc_table'; -import { SortPairArr } from '../../components/doc_table/utils/get_sort'; +import type { ISearchSource } from '@kbn/data-plugin/public'; +import type { SortOrder } from '@kbn/saved-search-plugin/public'; +import { getSortForSearchSource } from '../../utils/sorting'; export const updateSearchSource = ( searchSource: ISearchSource, dataView: DataView | undefined, - sort: (SortPairArr[] & string[][]) | undefined, + sort: (SortOrder[] & string[][]) | undefined, useNewFieldsApi: boolean, defaults: { sampleSize: number; diff --git a/src/plugins/discover/public/services/saved_searches/restore_from_saved_search.test.ts b/src/plugins/discover/public/services/saved_searches/restore_from_saved_search.test.ts new file mode 100644 index 0000000000000..355f80b78b2ac --- /dev/null +++ b/src/plugins/discover/public/services/saved_searches/restore_from_saved_search.test.ts @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { TimefilterContract } from '@kbn/data-plugin/public'; +import type { TimeRange, RefreshInterval } from '@kbn/data-plugin/common'; +import type { SavedSearch } from '@kbn/saved-search-plugin/public'; +import { restoreStateFromSavedSearch } from './restore_from_saved_search'; + +describe('discover restore state from saved search', () => { + let timefilterMock: TimefilterContract; + const timeRange: TimeRange = { + from: 'now-30m', + to: 'now', + }; + const refreshInterval: RefreshInterval = { + value: 5000, + pause: false, + }; + + beforeEach(() => { + timefilterMock = { + setTime: jest.fn(), + setRefreshInterval: jest.fn(), + } as unknown as TimefilterContract; + }); + + test('should not update timefilter if attributes are not set', async () => { + restoreStateFromSavedSearch({ + savedSearch: {} as SavedSearch, + timefilter: timefilterMock, + }); + + expect(timefilterMock.setTime).not.toHaveBeenCalled(); + expect(timefilterMock.setRefreshInterval).not.toHaveBeenCalled(); + }); + + test('should not update timefilter if timeRestore is disabled', async () => { + restoreStateFromSavedSearch({ + savedSearch: { + timeRestore: false, + timeRange, + refreshInterval, + } as SavedSearch, + timefilter: timefilterMock, + }); + + expect(timefilterMock.setTime).not.toHaveBeenCalled(); + expect(timefilterMock.setRefreshInterval).not.toHaveBeenCalled(); + }); + + test('should update timefilter if timeRestore is enabled', async () => { + restoreStateFromSavedSearch({ + savedSearch: { + timeRestore: true, + timeRange, + refreshInterval, + } as SavedSearch, + timefilter: timefilterMock, + }); + + expect(timefilterMock.setTime).toHaveBeenCalledWith(timeRange); + expect(timefilterMock.setRefreshInterval).toHaveBeenCalledWith(refreshInterval); + }); + + test('should not update timefilter if attributes are missing', async () => { + restoreStateFromSavedSearch({ + savedSearch: { + timeRestore: true, + } as SavedSearch, + timefilter: timefilterMock, + }); + + expect(timefilterMock.setTime).not.toHaveBeenCalled(); + expect(timefilterMock.setRefreshInterval).not.toHaveBeenCalled(); + }); + + test('should not update timefilter if attributes are invalid', async () => { + restoreStateFromSavedSearch({ + savedSearch: { + timeRestore: true, + timeRange: { + from: 'test', + to: 'now', + }, + refreshInterval: { + pause: false, + value: -500, + }, + } as SavedSearch, + timefilter: timefilterMock, + }); + + expect(timefilterMock.setTime).not.toHaveBeenCalled(); + expect(timefilterMock.setRefreshInterval).not.toHaveBeenCalled(); + }); +}); diff --git a/src/plugins/discover/public/services/saved_searches/restore_from_saved_search.ts b/src/plugins/discover/public/services/saved_searches/restore_from_saved_search.ts new file mode 100644 index 0000000000000..550c36408977b --- /dev/null +++ b/src/plugins/discover/public/services/saved_searches/restore_from_saved_search.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { TimefilterContract } from '@kbn/data-plugin/public'; +import type { SavedSearch } from '@kbn/saved-search-plugin/public'; +import { isRefreshIntervalValid, isTimeRangeValid } from '../../utils/validate_time'; + +export const restoreStateFromSavedSearch = ({ + savedSearch, + timefilter, +}: { + savedSearch: SavedSearch; + timefilter: TimefilterContract; +}) => { + if (!savedSearch) { + return; + } + + if (savedSearch.timeRestore && savedSearch.timeRange && isTimeRangeValid(savedSearch.timeRange)) { + timefilter.setTime(savedSearch.timeRange); + } + if ( + savedSearch.timeRestore && + savedSearch.refreshInterval && + isRefreshIntervalValid(savedSearch.refreshInterval) + ) { + timefilter.setRefreshInterval(savedSearch.refreshInterval); + } +}; diff --git a/src/plugins/discover/public/utils/get_sharing_data.ts b/src/plugins/discover/public/utils/get_sharing_data.ts index 6d99edd3d9fe1..ace169359e318 100644 --- a/src/plugins/discover/public/utils/get_sharing_data.ts +++ b/src/plugins/discover/public/utils/get_sharing_data.ts @@ -15,12 +15,12 @@ import type { } from '@kbn/data-plugin/public'; import type { Filter } from '@kbn/es-query'; import type { SavedSearch, SortOrder } from '@kbn/saved-search-plugin/public'; +import { getSortForSearchSource } from './sorting'; import { DOC_HIDE_TIME_COLUMN_SETTING, SEARCH_FIELDS_FROM_SOURCE, SORT_DEFAULT_ORDER_SETTING, } from '../../common'; -import { getSortForSearchSource } from '../components/doc_table'; import { AppState, isEqualFilters } from '../application/main/services/discover_state'; /** diff --git a/src/plugins/discover/public/components/doc_table/utils/get_default_sort.test.ts b/src/plugins/discover/public/utils/sorting/get_default_sort.test.ts similarity index 100% rename from src/plugins/discover/public/components/doc_table/utils/get_default_sort.test.ts rename to src/plugins/discover/public/utils/sorting/get_default_sort.test.ts diff --git a/src/plugins/discover/public/components/doc_table/utils/get_default_sort.ts b/src/plugins/discover/public/utils/sorting/get_default_sort.ts similarity index 93% rename from src/plugins/discover/public/components/doc_table/utils/get_default_sort.ts rename to src/plugins/discover/public/utils/sorting/get_default_sort.ts index 82055e94f5407..d5b72c4b13949 100644 --- a/src/plugins/discover/public/components/doc_table/utils/get_default_sort.ts +++ b/src/plugins/discover/public/utils/sorting/get_default_sort.ts @@ -7,8 +7,8 @@ */ import type { DataView } from '@kbn/data-views-plugin/public'; +import type { SortOrder } from '@kbn/saved-search-plugin/public'; import { isSortable } from './get_sort'; -import { SortOrder } from '../components/table_header/helpers'; /** * use in case the user didn't manually sort. diff --git a/src/plugins/discover/public/components/doc_table/utils/get_sort.test.ts b/src/plugins/discover/public/utils/sorting/get_sort.test.ts similarity index 66% rename from src/plugins/discover/public/components/doc_table/utils/get_sort.test.ts rename to src/plugins/discover/public/utils/sorting/get_sort.test.ts index f778bbd8fecaa..0656a2e36c4ca 100644 --- a/src/plugins/discover/public/components/doc_table/utils/get_sort.test.ts +++ b/src/plugins/discover/public/utils/sorting/get_sort.test.ts @@ -6,11 +6,12 @@ * Side Public License, v 1. */ -import { getSort, getSortArray } from './get_sort'; +import { getSort, getSortArray, getSortForEmbeddable } from './get_sort'; import { stubDataView, stubDataViewWithoutTimeField, } from '@kbn/data-views-plugin/common/data_view.stub'; +import { uiSettingsMock } from '../../__mocks__/ui_settings'; describe('docTable', function () { describe('getSort function', function () { @@ -47,7 +48,6 @@ describe('docTable', function () { expect(getSort(['foo', 'asc'], stubDataView)).toEqual([{ foo: 'asc' }]); }); }); - describe('getSortArray function', function () { test('should have an array method', function () { expect(getSortArray).toBeInstanceOf(Function); @@ -64,14 +64,42 @@ describe('docTable', function () { test('should sort by an empty array when an unsortable field is given', function () { expect(getSortArray([{ 'non-sortable': 'asc' }], stubDataView)).toEqual([]); expect(getSortArray([{ lol_nope: 'asc' }], stubDataView)).toEqual([]); - expect(getSortArray([{ 'non-sortable': 'asc' }], stubDataViewWithoutTimeField)).toEqual([]); }); test('should return an empty array when passed an empty sort array', () => { expect(getSortArray([], stubDataView)).toEqual([]); - expect(getSortArray([], stubDataViewWithoutTimeField)).toEqual([]); }); }); + describe('getSortForEmbeddable function', function () { + test('should return an array of arrays for sortable fields', function () { + expect(getSortForEmbeddable([['bytes', 'desc']], stubDataView)).toEqual([['bytes', 'desc']]); + }); + + test('should return an array of arrays from an array of elasticsearch sort objects', function () { + expect(getSortForEmbeddable([{ bytes: 'desc' }], stubDataView)).toEqual([['bytes', 'desc']]); + }); + + test('should sort by an empty array when an unsortable field is given', function () { + expect(getSortForEmbeddable([{ 'non-sortable': 'asc' }], stubDataView)).toEqual([]); + expect(getSortForEmbeddable([{ lol_nope: 'asc' }], stubDataView)).toEqual([]); + expect( + getSortForEmbeddable([{ 'non-sortable': 'asc' }], stubDataViewWithoutTimeField) + ).toEqual([]); + }); + + test('should return an empty array when passed an empty sort array', () => { + expect(getSortForEmbeddable([], stubDataView)).toEqual([]); + expect(getSortForEmbeddable([], stubDataViewWithoutTimeField)).toEqual([]); + }); + + test('should provide fallback results', () => { + expect(getSortForEmbeddable(undefined)).toEqual([]); + expect(getSortForEmbeddable(undefined, stubDataView)).toEqual([]); + expect(getSortForEmbeddable(undefined, stubDataView, uiSettingsMock)).toEqual([ + ['@timestamp', 'desc'], + ]); + }); + }); }); diff --git a/src/plugins/discover/public/components/doc_table/utils/get_sort.ts b/src/plugins/discover/public/utils/sorting/get_sort.ts similarity index 67% rename from src/plugins/discover/public/components/doc_table/utils/get_sort.ts rename to src/plugins/discover/public/utils/sorting/get_sort.ts index 31d4375a5f266..59d414d8e1eea 100644 --- a/src/plugins/discover/public/components/doc_table/utils/get_sort.ts +++ b/src/plugins/discover/public/utils/sorting/get_sort.ts @@ -8,10 +8,13 @@ import { isPlainObject } from 'lodash'; import { DataView } from '@kbn/data-views-plugin/public'; +import { IUiSettingsClient } from '@kbn/core/public'; +import type { SortOrder } from '@kbn/saved-search-plugin/public'; +import { DOC_HIDE_TIME_COLUMN_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../common'; +import { getDefaultSort } from './get_default_sort'; export type SortPairObj = Record; -export type SortPairArr = [string, string]; -export type SortPair = SortPairArr | SortPairObj; +export type SortPair = SortOrder | SortPairObj; export type SortInput = SortPair | SortPair[]; export function isSortable(fieldName: string, dataView: DataView): boolean { @@ -25,7 +28,7 @@ function createSortObject(sortPair: SortInput, dataView: DataView): SortPairObj sortPair.length === 2 && isSortable(String(sortPair[0]), dataView) ) { - const [field, direction] = sortPair as SortPairArr; + const [field, direction] = sortPair as SortOrder; return { [field]: direction }; } else if (isPlainObject(sortPair) && isSortable(Object.keys(sortPair)[0], dataView)) { return sortPair as SortPairObj; @@ -62,8 +65,8 @@ export function getSort(sort: SortPair[] | SortPair, dataView: DataView): SortPa * compared to getSort it doesn't return an array of objects, it returns an array of arrays * [[fieldToSort: directionToSort]] */ -export function getSortArray(sort: SortPair[], dataView: DataView): SortPairArr[] { - return getSort(sort, dataView).reduce((acc: SortPairArr[], sortPair) => { +export function getSortArray(sort: SortInput, dataView: DataView): SortOrder[] { + return getSort(sort, dataView).reduce((acc: SortOrder[], sortPair) => { const entries = Object.entries(sortPair); if (entries && entries[0]) { acc.push(entries[0]); @@ -71,3 +74,22 @@ export function getSortArray(sort: SortPair[], dataView: DataView): SortPairArr[ return acc; }, []); } + +/** + * sorting for embeddable, like getSortArray,but returning a default in the case the given sort or dataView is not valid + */ +export function getSortForEmbeddable( + sort?: SortInput, + dataView?: DataView, + uiSettings?: IUiSettingsClient +): SortOrder[] { + if (!sort || !sort.length || !dataView) { + if (!uiSettings) { + return []; + } + const defaultSortOrder = uiSettings.get(SORT_DEFAULT_ORDER_SETTING, 'desc'); + const hidingTimeColumn = uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false); + return getDefaultSort(dataView, defaultSortOrder, hidingTimeColumn); + } + return getSortArray(sort, dataView); +} diff --git a/src/plugins/discover/public/components/doc_table/utils/get_sort_for_search_source.test.ts b/src/plugins/discover/public/utils/sorting/get_sort_for_search_source.test.ts similarity index 96% rename from src/plugins/discover/public/components/doc_table/utils/get_sort_for_search_source.test.ts rename to src/plugins/discover/public/utils/sorting/get_sort_for_search_source.test.ts index 729d74cb710fa..dd54b5d2a70a2 100644 --- a/src/plugins/discover/public/components/doc_table/utils/get_sort_for_search_source.test.ts +++ b/src/plugins/discover/public/utils/sorting/get_sort_for_search_source.test.ts @@ -5,9 +5,8 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - +import type { SortOrder } from '@kbn/saved-search-plugin/public'; import { getSortForSearchSource } from './get_sort_for_search_source'; -import { SortOrder } from '../components/table_header/helpers'; import { stubDataView, stubDataViewWithoutTimeField } from '@kbn/data-plugin/common/stubs'; describe('getSortForSearchSource function', function () { diff --git a/src/plugins/discover/public/components/doc_table/utils/get_sort_for_search_source.ts b/src/plugins/discover/public/utils/sorting/get_sort_for_search_source.ts similarity index 96% rename from src/plugins/discover/public/components/doc_table/utils/get_sort_for_search_source.ts rename to src/plugins/discover/public/utils/sorting/get_sort_for_search_source.ts index c3f8badc0a536..bcf0ccf4d0e30 100644 --- a/src/plugins/discover/public/components/doc_table/utils/get_sort_for_search_source.ts +++ b/src/plugins/discover/public/utils/sorting/get_sort_for_search_source.ts @@ -8,7 +8,7 @@ import type { DataView } from '@kbn/data-views-plugin/public'; import type { EsQuerySortValue } from '@kbn/data-plugin/public'; -import { SortOrder } from '../components/table_header/helpers'; +import type { SortOrder } from '@kbn/saved-search-plugin/public'; import { getSort } from './get_sort'; /** diff --git a/src/plugins/discover/public/utils/sorting/index.ts b/src/plugins/discover/public/utils/sorting/index.ts new file mode 100644 index 0000000000000..1d7f3ce6d671a --- /dev/null +++ b/src/plugins/discover/public/utils/sorting/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 { getSort, getSortArray, getSortForEmbeddable } from './get_sort'; +export { getSortForSearchSource } from './get_sort_for_search_source'; +export { getDefaultSort } from './get_default_sort'; +export type { SortPair } from './get_sort'; diff --git a/src/plugins/discover/public/utils/validate_time.test.ts b/src/plugins/discover/public/utils/validate_time.test.ts new file mode 100644 index 0000000000000..771c2a7bb029f --- /dev/null +++ b/src/plugins/discover/public/utils/validate_time.test.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { RefreshInterval, TimeRange } from '@kbn/data-plugin/common'; +import { isTimeRangeValid, isRefreshIntervalValid } from './validate_time'; + +describe('discover validate time', () => { + test('should validate time ranges correctly', async () => { + expect(isTimeRangeValid({ from: '2020-06-02T13:36:13.689Z', to: 'now' })).toEqual(true); + expect(isTimeRangeValid({ from: 'now', to: 'now+1h' })).toEqual(true); + expect(isTimeRangeValid({ from: '', to: '' })).toEqual(false); + expect(isTimeRangeValid({} as unknown as TimeRange)).toEqual(false); + expect(isTimeRangeValid(undefined)).toEqual(false); + }); + + test('should validate that refresh interval is valid', async () => { + expect(isRefreshIntervalValid({ value: 5000, pause: false })).toEqual(true); + expect(isRefreshIntervalValid({ value: 0, pause: false })).toEqual(true); + expect(isRefreshIntervalValid({ value: 4000, pause: true })).toEqual(true); + }); + + test('should validate that refresh interval is invalid', async () => { + expect(isRefreshIntervalValid({ value: -5000, pause: false })).toEqual(false); + expect( + isRefreshIntervalValid({ value: 'test', pause: false } as unknown as RefreshInterval) + ).toEqual(false); + expect( + isRefreshIntervalValid({ value: 4000, pause: 'test' } as unknown as RefreshInterval) + ).toEqual(false); + expect(isRefreshIntervalValid({} as unknown as RefreshInterval)).toEqual(false); + expect(isRefreshIntervalValid(undefined)).toEqual(false); + }); +}); diff --git a/src/plugins/discover/public/utils/validate_time.ts b/src/plugins/discover/public/utils/validate_time.ts new file mode 100644 index 0000000000000..a6ad44e2112c6 --- /dev/null +++ b/src/plugins/discover/public/utils/validate_time.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 dateMath from '@kbn/datemath'; +import type { RefreshInterval } from '@kbn/data-plugin/common'; + +export function isTimeRangeValid(timeRange?: { from: string; to: string }): boolean { + if (!timeRange?.from || !timeRange?.to) { + return false; + } + const fromMoment = dateMath.parse(timeRange.from); + const toMoment = dateMath.parse(timeRange.to); + return Boolean(fromMoment && toMoment && fromMoment.isValid() && toMoment.isValid()); +} + +export function isRefreshIntervalValid(refreshInterval?: RefreshInterval): boolean { + if (!refreshInterval) { + return false; + } + return ( + typeof refreshInterval?.value === 'number' && + refreshInterval?.value >= 0 && + typeof refreshInterval?.pause === 'boolean' + ); +} diff --git a/src/plugins/kibana_react/public/code_editor/__snapshots__/code_editor.test.tsx.snap b/src/plugins/kibana_react/public/code_editor/__snapshots__/code_editor.test.tsx.snap index 9a4511f8b03f5..6d077e6e174e5 100644 --- a/src/plugins/kibana_react/public/code_editor/__snapshots__/code_editor.test.tsx.snap +++ b/src/plugins/kibana_react/public/code_editor/__snapshots__/code_editor.test.tsx.snap @@ -226,21 +226,6 @@ exports[` is rendered 1`] = ` - - -
- -
`; diff --git a/src/plugins/kibana_react/public/code_editor/code_editor.test.tsx b/src/plugins/kibana_react/public/code_editor/code_editor.test.tsx index b66d136c50710..97a8d8a849083 100644 --- a/src/plugins/kibana_react/public/code_editor/code_editor.test.tsx +++ b/src/plugins/kibana_react/public/code_editor/code_editor.test.tsx @@ -43,7 +43,15 @@ const logs = ` [Sun Mar 7 21:16:17 2004] [error] [client xx.xx.xx.xx] File does not exist: /home/httpd/twiki/view/Main/WebHome `; +class ResizeObserver { + observe() {} + unobserve() {} + disconnect() {} +} + describe('', () => { + window.ResizeObserver = ResizeObserver; + test('is rendered', () => { const component = mountWithIntl( {}} /> diff --git a/src/plugins/kibana_react/public/code_editor/code_editor.tsx b/src/plugins/kibana_react/public/code_editor/code_editor.tsx index 239208e6752fc..93a2c6333f9cc 100644 --- a/src/plugins/kibana_react/public/code_editor/code_editor.tsx +++ b/src/plugins/kibana_react/public/code_editor/code_editor.tsx @@ -7,7 +7,7 @@ */ import React, { useState, useRef, useCallback, useMemo, useEffect, KeyboardEvent } from 'react'; -import ReactResizeDetector from 'react-resize-detector'; +import { useResizeDetector } from 'react-resize-detector'; import ReactMonacoEditor from 'react-monaco-editor'; import { htmlIdGenerator, @@ -186,6 +186,13 @@ export const CodeEditor: React.FC = ({ _editor.current?.layout(); }, []); + useResizeDetector({ + handleWidth: true, + handleHeight: true, + onResize: _updateDimensions, + refreshMode: 'debounce', + }); + const startEditing = useCallback(() => { setIsHintActive(false); _editor.current?.focus(); @@ -469,12 +476,6 @@ export const CodeEditor: React.FC = ({ }} /> - ); }; diff --git a/src/plugins/kibana_react/public/markdown/markdown_simple.tsx b/src/plugins/kibana_react/public/markdown/markdown_simple.tsx index 23a00fdec038b..24b9eee7b1ca5 100644 --- a/src/plugins/kibana_react/public/markdown/markdown_simple.tsx +++ b/src/plugins/kibana_react/public/markdown/markdown_simple.tsx @@ -6,20 +6,16 @@ * Side Public License, v 1. */ -import React, { Fragment } from 'react'; +import React from 'react'; import ReactMarkdown from 'react-markdown'; -const markdownRenderers = { - root: Fragment, -}; - export interface MarkdownSimpleProps { children: string; } // Render markdown string into JSX inside of a Fragment. export const MarkdownSimple = ({ children }: MarkdownSimpleProps) => ( - {children} + {children} ); // Needed for React.lazy diff --git a/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx b/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx index b5e81707a7a79..452ef49881578 100644 --- a/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx +++ b/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { Fragment } from 'react'; +import React from 'react'; import { History } from 'history'; import { i18n } from '@kbn/i18n'; import { EuiLoadingSpinner } from '@elastic/eui'; @@ -20,7 +20,7 @@ import { KibanaThemeProvider } from '../theme'; const ReactMarkdown = React.lazy(() => import('react-markdown')); const ErrorRenderer = (props: { children: string }) => ( }> - + ); diff --git a/src/plugins/saved_search/public/services/saved_searches/get_saved_searches.test.ts b/src/plugins/saved_search/public/services/saved_searches/get_saved_searches.test.ts index 8ff4f6c1e9428..397f3c11990eb 100644 --- a/src/plugins/saved_search/public/services/saved_searches/get_saved_searches.test.ts +++ b/src/plugins/saved_search/public/services/saved_searches/get_saved_searches.test.ts @@ -105,6 +105,7 @@ describe('getSavedSearch', () => { "hideChart": false, "id": "ccf1af80-2297-11ec-86e0-1155ffb9c7a7", "isTextBasedQuery": undefined, + "refreshInterval": undefined, "rowHeight": undefined, "rowsPerPage": undefined, "searchSource": Object { @@ -145,6 +146,8 @@ describe('getSavedSearch', () => { "desc", ], ], + "timeRange": undefined, + "timeRestore": undefined, "title": "test1", "viewMode": undefined, } @@ -198,6 +201,7 @@ describe('getSavedSearch', () => { "hideChart": true, "id": "ccf1af80-2297-11ec-86e0-1155ffb9c7a7", "isTextBasedQuery": true, + "refreshInterval": undefined, "rowHeight": undefined, "rowsPerPage": undefined, "searchSource": Object { @@ -238,6 +242,8 @@ describe('getSavedSearch', () => { "desc", ], ], + "timeRange": undefined, + "timeRestore": undefined, "title": "test2", "viewMode": undefined, } diff --git a/src/plugins/saved_search/public/services/saved_searches/save_saved_searches.test.ts b/src/plugins/saved_search/public/services/saved_searches/save_saved_searches.test.ts index 0c88b2bcea94a..56b988b20121c 100644 --- a/src/plugins/saved_search/public/services/saved_searches/save_saved_searches.test.ts +++ b/src/plugins/saved_search/public/services/saved_searches/save_saved_searches.test.ts @@ -92,6 +92,7 @@ describe('saveSavedSearch', () => { kibanaSavedObjectMeta: { searchSourceJSON: '{}' }, sort: [], title: 'title', + timeRestore: false, }, { references: [] } ); @@ -112,6 +113,7 @@ describe('saveSavedSearch', () => { kibanaSavedObjectMeta: { searchSourceJSON: '{}' }, sort: [], title: 'title', + timeRestore: false, }, { references: [] } ); diff --git a/src/plugins/saved_search/public/services/saved_searches/saved_searches_utils.test.ts b/src/plugins/saved_search/public/services/saved_searches/saved_searches_utils.test.ts index 84a6c35de0c02..a6926aef50796 100644 --- a/src/plugins/saved_search/public/services/saved_searches/saved_searches_utils.test.ts +++ b/src/plugins/saved_search/public/services/saved_searches/saved_searches_utils.test.ts @@ -43,6 +43,7 @@ describe('saved_searches_utils', () => { "hideChart": true, "id": "id", "isTextBasedQuery": false, + "refreshInterval": undefined, "rowHeight": undefined, "rowsPerPage": undefined, "searchSource": SearchSource { @@ -66,6 +67,8 @@ describe('saved_searches_utils', () => { }, "sharingSavedObjectProps": Object {}, "sort": Array [], + "timeRange": undefined, + "timeRestore": undefined, "title": "saved search", "viewMode": undefined, } @@ -123,6 +126,7 @@ describe('saved_searches_utils', () => { "kibanaSavedObjectMeta": Object { "searchSourceJSON": "{}", }, + "refreshInterval": undefined, "rowHeight": undefined, "rowsPerPage": undefined, "sort": Array [ @@ -131,6 +135,8 @@ describe('saved_searches_utils', () => { "asc", ], ], + "timeRange": undefined, + "timeRestore": false, "title": "title", "viewMode": undefined, } diff --git a/src/plugins/saved_search/public/services/saved_searches/saved_searches_utils.ts b/src/plugins/saved_search/public/services/saved_searches/saved_searches_utils.ts index cb20b24287943..52c25783c0e50 100644 --- a/src/plugins/saved_search/public/services/saved_searches/saved_searches_utils.ts +++ b/src/plugins/saved_search/public/services/saved_searches/saved_searches_utils.ts @@ -43,6 +43,9 @@ export const fromSavedSearchAttributes = ( hideAggregatedPreview: attributes.hideAggregatedPreview, rowHeight: attributes.rowHeight, isTextBasedQuery: attributes.isTextBasedQuery, + timeRestore: attributes.timeRestore, + timeRange: attributes.timeRange, + refreshInterval: attributes.refreshInterval, rowsPerPage: attributes.rowsPerPage, }); @@ -61,5 +64,8 @@ export const toSavedSearchAttributes = ( hideAggregatedPreview: savedSearch.hideAggregatedPreview, rowHeight: savedSearch.rowHeight, isTextBasedQuery: savedSearch.isTextBasedQuery ?? false, + timeRestore: savedSearch.timeRestore ?? false, + timeRange: savedSearch.timeRange, + refreshInterval: savedSearch.refreshInterval, rowsPerPage: savedSearch.rowsPerPage, }); diff --git a/src/plugins/saved_search/public/services/saved_searches/types.ts b/src/plugins/saved_search/public/services/saved_searches/types.ts index e0c1e8be4a3ed..acd745374e265 100644 --- a/src/plugins/saved_search/public/services/saved_searches/types.ts +++ b/src/plugins/saved_search/public/services/saved_searches/types.ts @@ -7,7 +7,7 @@ */ import type { ResolvedSimpleSavedObject } from '@kbn/core/public'; -import type { ISearchSource } from '@kbn/data-plugin/public'; +import type { ISearchSource, RefreshInterval, TimeRange } from '@kbn/data-plugin/common'; export enum VIEW_MODE { DOCUMENT_LEVEL = 'documents', @@ -39,6 +39,11 @@ export interface SavedSearchAttributes { viewMode?: VIEW_MODE; hideAggregatedPreview?: boolean; rowHeight?: number; + + timeRestore?: boolean; + timeRange?: TimeRange; + refreshInterval?: RefreshInterval; + rowsPerPage?: number; } @@ -67,5 +72,11 @@ export interface SavedSearch { hideAggregatedPreview?: boolean; rowHeight?: number; isTextBasedQuery?: boolean; + + // for restoring time range with a saved search + timeRestore?: boolean; + timeRange?: TimeRange; + refreshInterval?: RefreshInterval; + rowsPerPage?: number; } diff --git a/src/plugins/saved_search/server/saved_objects/search.ts b/src/plugins/saved_search/server/saved_objects/search.ts index 85f6542f6b07d..dec1c852aee20 100644 --- a/src/plugins/saved_search/server/saved_objects/search.ts +++ b/src/plugins/saved_search/server/saved_objects/search.ts @@ -51,6 +51,21 @@ export function getSavedSearchObjectType( grid: { type: 'object', enabled: false }, version: { type: 'integer' }, rowHeight: { type: 'text' }, + timeRestore: { type: 'boolean', index: false, doc_values: false }, + timeRange: { + dynamic: false, + properties: { + from: { type: 'keyword', index: false, doc_values: false }, + to: { type: 'keyword', index: false, doc_values: false }, + }, + }, + refreshInterval: { + dynamic: false, + properties: { + pause: { type: 'boolean', index: false, doc_values: false }, + value: { type: 'integer', index: false, doc_values: false }, + }, + }, rowsPerPage: { type: 'integer', index: false, doc_values: false }, }, }, diff --git a/src/plugins/unified_field_list/README.md b/src/plugins/unified_field_list/README.md new file mode 100755 index 0000000000000..657523aad9c1b --- /dev/null +++ b/src/plugins/unified_field_list/README.md @@ -0,0 +1,21 @@ +# unifiedFieldList + +This Kibana plugin contains components and services for field list UI (as in fields sidebar on Discover and Lens pages). + +--- + +## Components + +* `` - loads and renders stats (Top values, Histogram) for a data view field. + +## Public Services + +* `loadStats(...)` - returns the loaded field stats (can also work with Ad-hoc data views) + +## Server APIs + +* `/api/unified_field_list/field_stats` - returns the loaded field stats (except for Ad-hoc data views) + +## Development + +See the [kibana contributing guide](https://github.com/elastic/kibana/blob/main/CONTRIBUTING.md) for instructions setting up your development environment. diff --git a/src/plugins/unified_field_list/common/constants.ts b/src/plugins/unified_field_list/common/constants.ts new file mode 100644 index 0000000000000..ef8a01b2705ff --- /dev/null +++ b/src/plugins/unified_field_list/common/constants.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 const BASE_API_PATH = '/api/unified_field_list'; +export const FIELD_STATS_API_PATH = `${BASE_API_PATH}/field_stats`; diff --git a/src/plugins/controls/public/control_types/index.ts b/src/plugins/unified_field_list/common/index.ts old mode 100644 new mode 100755 similarity index 88% rename from src/plugins/controls/public/control_types/index.ts rename to src/plugins/unified_field_list/common/index.ts index 8dfa50ee5cbda..747db7a56bbae --- a/src/plugins/controls/public/control_types/index.ts +++ b/src/plugins/unified_field_list/common/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export * from './range_slider'; +export const PLUGIN_ID = 'unifiedFieldList'; diff --git a/src/plugins/unified_field_list/common/types/index.ts b/src/plugins/unified_field_list/common/types/index.ts new file mode 100755 index 0000000000000..78dbc548406c0 --- /dev/null +++ b/src/plugins/unified_field_list/common/types/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 type { + FieldStatsResponse, + NumberStatsResult, + TopValuesResult, + BucketedAggregation, +} from './stats'; diff --git a/x-pack/plugins/lens/common/api.ts b/src/plugins/unified_field_list/common/types/stats.ts similarity index 84% rename from x-pack/plugins/lens/common/api.ts rename to src/plugins/unified_field_list/common/types/stats.ts index 026f540cdb67b..71d75db1aaaa3 100644 --- a/x-pack/plugins/lens/common/api.ts +++ b/src/plugins/unified_field_list/common/types/stats.ts @@ -1,8 +1,9 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 interface BucketedAggregation { diff --git a/x-pack/plugins/lens/server/routes/field_stats.ts b/src/plugins/unified_field_list/common/utils/field_stats_utils.ts similarity index 52% rename from x-pack/plugins/lens/server/routes/field_stats.ts rename to src/plugins/unified_field_list/common/utils/field_stats_utils.ts index 35a15ea44be67..9cf33961d76b6 100644 --- a/x-pack/plugins/lens/server/routes/field_stats.ts +++ b/src/plugins/unified_field_list/common/utils/field_stats_utils.ts @@ -1,149 +1,119 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 { errors } from '@elastic/elasticsearch'; + import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import DateMath from '@kbn/datemath'; -import { schema } from '@kbn/config-schema'; -import { CoreSetup } from '@kbn/core/server'; -import type { DataViewField } from '@kbn/data-views-plugin/common'; -import { SavedObjectNotFound } from '@kbn/kibana-utils-plugin/common'; import { ESSearchResponse } from '@kbn/core/types/elasticsearch'; -import { FieldStatsResponse, BASE_API_URL } from '../../common'; -import { PluginStartContract } from '../plugin'; +import type { DataViewFieldBase } from '@kbn/es-query'; +import type { FieldStatsResponse } from '../types'; -const SHARD_SIZE = 5000; +export type SearchHandler = ( + aggs: Record +) => Promise>; -export async function initFieldsRoute(setup: CoreSetup) { - const router = setup.http.createRouter(); - router.post( - { - path: `${BASE_API_URL}/index_stats/{indexPatternId}/field`, - validate: { - params: schema.object({ - indexPatternId: schema.string(), - }), - body: schema.object( - { - dslQuery: schema.object({}, { unknowns: 'allow' }), - fromDate: schema.string(), - toDate: schema.string(), - fieldName: schema.string(), - size: schema.maybe(schema.number()), +const SHARD_SIZE = 5000; +const DEFAULT_TOP_VALUES_SIZE = 10; + +export function buildSearchParams({ + dataViewPattern, + timeFieldName, + fromDate, + toDate, + dslQuery, + runtimeMappings, + aggs, +}: { + dataViewPattern: string; + timeFieldName?: string; + fromDate: string; + toDate: string; + dslQuery: object; + runtimeMappings: estypes.MappingRuntimeFields; + aggs: Record; +}) { + const filter = timeFieldName + ? [ + { + range: { + [timeFieldName]: { + gte: fromDate, + lte: toDate, + }, }, - { unknowns: 'allow' } - ), - }, + }, + dslQuery, + ] + : [dslQuery]; + + const query = { + bool: { + filter, }, - async (context, req, res) => { - const requestClient = (await context.core).elasticsearch.client.asCurrentUser; - const { fromDate, toDate, fieldName, dslQuery, size } = req.body; - - const [{ savedObjects, elasticsearch }, { dataViews }] = await setup.getStartServices(); - const savedObjectsClient = savedObjects.getScopedClient(req); - const esClient = elasticsearch.client.asScoped(req).asCurrentUser; - const indexPatternsService = await dataViews.dataViewsServiceFactory( - savedObjectsClient, - esClient - ); - - try { - const indexPattern = await indexPatternsService.get(req.params.indexPatternId); - - const timeFieldName = indexPattern.timeFieldName; - const field = indexPattern.fields.find((f) => f.name === fieldName); - - if (!field) { - throw new Error(`Field {fieldName} not found in data view ${indexPattern.title}`); - } - - const filter = timeFieldName - ? [ - { - range: { - [timeFieldName]: { - gte: fromDate, - lte: toDate, - }, - }, - }, - dslQuery, - ] - : [dslQuery]; - - const query = { - bool: { - filter, - }, - }; + }; - const runtimeMappings = indexPattern.getRuntimeMappings(); + return { + index: dataViewPattern, + body: { + query, + aggs, + runtime_mappings: runtimeMappings, + }, + track_total_hits: true, + size: 0, + }; +} - const search = async (aggs: Record) => { - const result = await requestClient.search({ - index: indexPattern.title, - track_total_hits: true, - body: { - query, - aggs, - runtime_mappings: runtimeMappings, - }, - size: 0, - }); - return result; - }; +export async function fetchAndCalculateFieldStats({ + searchHandler, + field, + fromDate, + toDate, + size, +}: { + searchHandler: SearchHandler; + field: DataViewFieldBase; + fromDate: string; + toDate: string; + size?: number; +}) { + if (!canProvideStatsForField(field)) { + return {}; + } - if (field.type.includes('range')) { - return res.ok({ body: {} }); - } - - if (field.type === 'histogram') { - return res.ok({ - body: await getNumberHistogram(search, field, false), - }); - } else if (field.type === 'number') { - return res.ok({ - body: await getNumberHistogram(search, field), - }); - } else if (field.type === 'date') { - return res.ok({ - body: await getDateHistogram(search, field, { fromDate, toDate }), - }); - } - - return res.ok({ - body: await getStringSamples(search, field, size), - }); - } catch (e) { - if (e instanceof SavedObjectNotFound) { - return res.notFound(); - } - if (e instanceof errors.ResponseError && e.statusCode === 404) { - return res.notFound(); - } - if (e.isBoom) { - if (e.output.statusCode === 404) { - return res.notFound(); - } - throw new Error(e.output.message); - } else { - throw e; - } - } - } + if (field.type === 'histogram') { + return await getNumberHistogram(searchHandler, field, false); + } + + if (field.type === 'number') { + return await getNumberHistogram(searchHandler, field); + } + + if (field.type === 'date') { + return await getDateHistogram(searchHandler, field, { fromDate, toDate }); + } + + return await getStringSamples(searchHandler, field, size); +} + +export function canProvideStatsForField(field: DataViewFieldBase): boolean { + return !( + field.type === 'document' || + field.type.includes('range') || + field.type === 'geo_point' || + field.type === 'geo_shape' ); } export async function getNumberHistogram( - aggSearchWithBody: ( - aggs: Record - ) => Promise, - field: DataViewField, + aggSearchWithBody: SearchHandler, + field: DataViewFieldBase, useTopHits = true -): Promise { +): Promise> { const fieldRef = getFieldRef(field); const baseAggs = { @@ -167,7 +137,7 @@ export async function getNumberHistogram( aggs: { ...baseAggs, top_values: { - terms: { ...fieldRef, size: 10 }, + terms: { ...fieldRef, size: DEFAULT_TOP_VALUES_SIZE }, }, }, }, @@ -203,7 +173,7 @@ export async function getNumberHistogram( if (histogramInterval === 0) { return { - totalDocuments: minMaxResult.hits.total.value, + totalDocuments: getHitsTotal(minMaxResult), sampledValues: minMaxResult.aggregations!.sample.sample_count.value!, sampledDocuments: minMaxResult.aggregations!.sample.doc_count, topValues: topValuesBuckets, @@ -211,7 +181,7 @@ export async function getNumberHistogram( ? { buckets: [] } : { // Insert a fake bucket for a single-value histogram - buckets: [{ count: minMaxResult.aggregations!.sample.doc_count, key: minValue }], + buckets: [{ count: minMaxResult.aggregations!.sample.doc_count, key: minValue! }], }, }; } @@ -235,7 +205,7 @@ export async function getNumberHistogram( >; return { - totalDocuments: minMaxResult.hits.total.value, + totalDocuments: getHitsTotal(minMaxResult), sampledDocuments: minMaxResult.aggregations!.sample.doc_count, sampledValues: minMaxResult.aggregations!.sample.sample_count.value!, histogram: { @@ -249,10 +219,10 @@ export async function getNumberHistogram( } export async function getStringSamples( - aggSearchWithBody: (aggs: Record) => unknown, - field: DataViewField, - size = 10 -): Promise { + aggSearchWithBody: SearchHandler, + field: DataViewFieldBase, + size = DEFAULT_TOP_VALUES_SIZE +): Promise> { const fieldRef = getFieldRef(field); const topValuesBody = { @@ -275,7 +245,7 @@ export async function getStringSamples( >; return { - totalDocuments: topValuesResult.hits.total.value, + totalDocuments: getHitsTotal(topValuesResult), sampledDocuments: topValuesResult.aggregations!.sample.doc_count, sampledValues: topValuesResult.aggregations!.sample.sample_count.value!, topValues: { @@ -289,10 +259,10 @@ export async function getStringSamples( // This one is not sampled so that it returns the full date range export async function getDateHistogram( - aggSearchWithBody: (aggs: Record) => unknown, - field: DataViewField, + aggSearchWithBody: SearchHandler, + field: DataViewFieldBase, range: { fromDate: string; toDate: string } -): Promise { +): Promise> { const fromDate = DateMath.parse(range.fromDate); const toDate = DateMath.parse(range.toDate); if (!fromDate) { @@ -322,7 +292,7 @@ export async function getDateHistogram( >; return { - totalDocuments: results.hits.total.value, + totalDocuments: getHitsTotal(results), histogram: { buckets: results.aggregations!.histo.buckets.map((bucket) => ({ count: bucket.doc_count, @@ -332,7 +302,7 @@ export async function getDateHistogram( }; } -function getFieldRef(field: DataViewField) { +function getFieldRef(field: DataViewFieldBase) { return field.scripted ? { script: { @@ -342,3 +312,7 @@ function getFieldRef(field: DataViewField) { } : { field: field.name }; } + +const getHitsTotal = (body: estypes.SearchResponse): number => { + return (body.hits.total as estypes.SearchTotalHits).value ?? body.hits.total ?? 0; +}; diff --git a/src/plugins/controls/public/control_types/range_slider/index.ts b/src/plugins/unified_field_list/jest.config.js similarity index 50% rename from src/plugins/controls/public/control_types/range_slider/index.ts rename to src/plugins/unified_field_list/jest.config.js index 5fdd29672b86f..a1782360c93b9 100644 --- a/src/plugins/controls/public/control_types/range_slider/index.ts +++ b/src/plugins/unified_field_list/jest.config.js @@ -6,8 +6,11 @@ * Side Public License, v 1. */ -export { RANGE_SLIDER_CONTROL } from '../../../common/control_types/range_slider/types'; -export { RangeSliderEmbeddableFactory } from './range_slider_embeddable_factory'; - -export type { RangeSliderEmbeddable } from './range_slider_embeddable'; -export type { RangeSliderEmbeddableInput } from '../../../common/control_types/range_slider/types'; +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/unified_field_list'], + coverageDirectory: '/target/kibana-coverage/jest/src/plugins/unified_field_list', + coverageReporters: ['text', 'html'], + collectCoverageFrom: ['/src/plugins/unified_field_list/public/**/*.{ts,tsx}'], +}; diff --git a/src/plugins/unified_field_list/kibana.json b/src/plugins/unified_field_list/kibana.json new file mode 100755 index 0000000000000..6785d55989cc5 --- /dev/null +++ b/src/plugins/unified_field_list/kibana.json @@ -0,0 +1,15 @@ +{ + "id": "unifiedFieldList", + "version": "1.0.0", + "kibanaVersion": "kibana", + "owner": { + "name": "Data Discovery", + "githubTeam": "kibana-data-discovery" + }, + "description": "Contains functionality for the field list which can be integrated into apps", + "server": true, + "ui": true, + "requiredPlugins": ["dataViews", "data", "fieldFormats", "charts"], + "optionalPlugins": [], + "requiredBundles": [] +} diff --git a/src/plugins/unified_field_list/public/components/field_stats/field_stats.test.tsx b/src/plugins/unified_field_list/public/components/field_stats/field_stats.test.tsx new file mode 100644 index 0000000000000..87b8b7530c839 --- /dev/null +++ b/src/plugins/unified_field_list/public/components/field_stats/field_stats.test.tsx @@ -0,0 +1,543 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { act } from 'react-dom/test-utils'; +import { EuiLoadingSpinner, EuiProgress } from '@elastic/eui'; +import { coreMock } from '@kbn/core/public/mocks'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { fieldFormatsServiceMock } from '@kbn/field-formats-plugin/public/mocks'; +import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; +import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; +import type { DataView } from '@kbn/data-views-plugin/common'; +import type { DataViewField } from '@kbn/data-views-plugin/common'; +import { loadFieldStats } from '../../services/field_stats'; +import FieldStats from './field_stats'; +import type { FieldStatsProps } from './field_stats'; + +jest.mock('../../services/field_stats', () => ({ + loadFieldStats: jest.fn().mockResolvedValue({}), +})); + +const mockedServices = { + data: dataPluginMock.createStartContract(), + dataViews: dataViewPluginMocks.createStartContract(), + fieldFormats: fieldFormatsServiceMock.createStartContract(), + charts: chartPluginMock.createSetupContract(), + uiSettings: coreMock.createStart().uiSettings, +}; + +describe('UnifiedFieldList ', () => { + let defaultProps: FieldStatsProps; + let dataView: DataView; + + beforeEach(() => { + dataView = { + id: '1', + title: 'my-fake-index-pattern', + timeFieldName: 'timestamp', + fields: [ + { + name: 'timestamp', + displayName: 'timestampLabel', + type: 'date', + aggregatable: true, + searchable: true, + }, + { + name: 'bytes', + displayName: 'bytesLabel', + type: 'number', + aggregatable: true, + searchable: true, + }, + { + name: 'memory', + displayName: 'memory', + type: 'number', + aggregatable: true, + searchable: true, + }, + { + name: 'unsupported', + displayName: 'unsupported', + type: 'geo', + aggregatable: true, + searchable: true, + }, + { + name: 'source', + displayName: 'source', + type: 'string', + aggregatable: true, + searchable: true, + }, + { + name: 'ip_range', + displayName: 'ip_range', + type: 'ip_range', + aggregatable: true, + searchable: true, + }, + { + name: 'machine.ram', + displayName: 'machine.ram', + type: 'number', + aggregatable: true, + searchable: true, + }, + ], + getFormatterForField: jest.fn(() => ({ + convert: jest.fn((s: unknown) => JSON.stringify(s)), + })), + } as unknown as DataView; + + defaultProps = { + services: mockedServices, + dataViewOrDataViewId: dataView, + field: { + name: 'bytes', + type: 'number', + } as unknown as DataViewField, + fromDate: 'now-7d', + toDate: 'now', + query: { query: '', language: 'lucene' }, + filters: [], + 'data-test-subj': 'testing', + }; + + (mockedServices.dataViews.get as jest.Mock).mockImplementation(() => { + return Promise.resolve(dataView); + }); + }); + + beforeEach(() => { + (loadFieldStats as jest.Mock).mockReset(); + (loadFieldStats as jest.Mock).mockImplementation(() => Promise.resolve({})); + }); + + it('should request field stats with correct params', async () => { + let resolveFunction: (arg: unknown) => void; + + (loadFieldStats as jest.Mock).mockImplementation(() => { + return new Promise((resolve) => { + resolveFunction = resolve; + }); + }); + + const wrapper = mountWithIntl( + + ); + + await wrapper.update(); + + expect(loadFieldStats).toHaveBeenCalledWith({ + abortController: new AbortController(), + services: { data: mockedServices.data }, + dataView, + dslQuery: { + bool: { + must: [], + filter: [ + { + bool: { + should: [{ match_phrase: { 'geo.src': 'US' } }], + minimum_should_match: 1, + }, + }, + { + match: { phrase: { 'geo.dest': 'US' } }, + }, + ], + should: [], + must_not: [], + }, + }, + fromDate: 'now-14d', + toDate: 'now-7d', + field: defaultProps.field, + }); + + expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(1); + + await act(async () => { + resolveFunction!({ + totalDocuments: 4633, + sampledDocuments: 4633, + sampledValues: 4633, + histogram: { + buckets: [{ count: 705, key: 0 }], + }, + topValues: { + buckets: [{ count: 147, key: 0 }], + }, + }); + }); + + await wrapper.update(); + + expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(0); + + expect(loadFieldStats).toHaveBeenCalledTimes(1); + }); + + it('should not request field stats for range fields', async () => { + const wrapper = await mountWithIntl( + + ); + + await wrapper.update(); + + expect(loadFieldStats).not.toHaveBeenCalled(); + }); + + it('should not request field stats for geo fields', async () => { + const wrapper = await mountWithIntl( + + ); + + await wrapper.update(); + + expect(loadFieldStats).not.toHaveBeenCalled(); + }); + + it('should render nothing if no data is found', async () => { + const wrapper = mountWithIntl(); + + await wrapper.update(); + + expect(loadFieldStats).toHaveBeenCalled(); + + expect(wrapper.text()).toBe(''); + }); + + it('should render Top Values field stats correctly for a keyword field', async () => { + let resolveFunction: (arg: unknown) => void; + + (loadFieldStats as jest.Mock).mockImplementation(() => { + return new Promise((resolve) => { + resolveFunction = resolve; + }); + }); + + const wrapper = mountWithIntl( + + ); + + await wrapper.update(); + + expect(loadFieldStats).toHaveBeenCalledWith({ + abortController: new AbortController(), + services: { data: mockedServices.data }, + dataView, + fromDate: 'now-7d', + toDate: 'now', + dslQuery: { + bool: { + must: [], + filter: [], + should: [], + must_not: [], + }, + }, + field: defaultProps.field, + }); + + expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(1); + + await act(async () => { + resolveFunction!({ + totalDocuments: 1624, + sampledDocuments: 1624, + sampledValues: 3248, + topValues: { + buckets: [ + { + count: 1349, + key: 'success', + }, + { + count: 1206, + key: 'info', + }, + { + count: 329, + key: 'security', + }, + { + count: 164, + key: 'warning', + }, + { + count: 111, + key: 'error', + }, + { + count: 89, + key: 'login', + }, + ], + }, + }); + }); + + await wrapper.update(); + + expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(0); + expect(wrapper.find(EuiProgress)).toHaveLength(6); + + expect(loadFieldStats).toHaveBeenCalledTimes(1); + + const stats = wrapper.find('[data-test-subj="testing-topValues"]'); + const firstValue = stats.childAt(0); + + expect(stats).toHaveLength(1); + expect(firstValue.find('[data-test-subj="testing-topValues-value"]').first().text()).toBe( + '"success"' + ); + expect(firstValue.find('[data-test-subj="testing-topValues-valueCount"]').first().text()).toBe( + '41.5%' + ); + + expect(wrapper.find('[data-test-subj="testing-statsFooter"]').first().text()).toBe( + '100% of 1624 documents' + ); + + expect(wrapper.text()).toBe( + 'Top values"success"41.5%"info"37.1%"security"10.1%"warning"5.0%"error"3.4%"login"2.7%100% of 1624 documents' + ); + }); + + it('should render Histogram field stats correctly for a date field', async () => { + let resolveFunction: (arg: unknown) => void; + + (loadFieldStats as jest.Mock).mockImplementation(() => { + return new Promise((resolve) => { + resolveFunction = resolve; + }); + }); + + const wrapper = mountWithIntl( + + ); + + await wrapper.update(); + + expect(loadFieldStats).toHaveBeenCalledWith({ + abortController: new AbortController(), + services: { data: mockedServices.data }, + dataView, + fromDate: 'now-1h', + toDate: 'now', + dslQuery: { + bool: { + must: [], + filter: [], + should: [], + must_not: [], + }, + }, + field: dataView.fields[0], + }); + + expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(1); + + await act(async () => { + resolveFunction!({ + totalDocuments: 13, + histogram: { + buckets: [ + { + count: 1, + key: 1660564080000, + }, + { + count: 2, + key: 1660564440000, + }, + { + count: 3, + key: 1660564800000, + }, + { + count: 1, + key: 1660565160000, + }, + { + count: 2, + key: 1660565520000, + }, + { + count: 0, + key: 1660565880000, + }, + { + count: 1, + key: 1660566240000, + }, + { + count: 1, + key: 1660566600000, + }, + { + count: 1, + key: 1660566960000, + }, + { + count: 1, + key: 1660567320000, + }, + ], + }, + }); + }); + + await wrapper.update(); + + expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(0); + + expect(loadFieldStats).toHaveBeenCalledTimes(1); + + expect(wrapper.find('[data-test-subj="testing-topValues"]')).toHaveLength(0); + expect(wrapper.find('[data-test-subj="testing-histogram"]')).toHaveLength(1); + expect(wrapper.find('[data-test-subj="testing-statsFooter"]').first().text()).toBe( + '13 documents' + ); + + expect(wrapper.text()).toBe('Time distribution13 documents'); + }); + + it('should render Top Values & Distribution field stats correctly for a number field', async () => { + let resolveFunction: (arg: unknown) => void; + + (loadFieldStats as jest.Mock).mockImplementation(() => { + return new Promise((resolve) => { + resolveFunction = resolve; + }); + }); + + const field = dataView.fields.find((f) => f.name === 'machine.ram')!; + + const wrapper = mountWithIntl( + + ); + + await wrapper.update(); + + expect(loadFieldStats).toHaveBeenCalledWith({ + abortController: new AbortController(), + services: { data: mockedServices.data }, + dataView, + fromDate: 'now-1h', + toDate: 'now', + dslQuery: { + bool: { + must: [], + filter: [], + should: [], + must_not: [], + }, + }, + field, + }); + + expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(1); + + await act(async () => { + resolveFunction!({ + totalDocuments: 23, + sampledDocuments: 23, + sampledValues: 23, + histogram: { + buckets: [ + { + count: 17, + key: 12, + }, + { + count: 6, + key: 13, + }, + ], + }, + topValues: { + buckets: [ + { + count: 17, + key: 12, + }, + { + count: 6, + key: 13, + }, + ], + }, + }); + }); + + await wrapper.update(); + + expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(0); + + expect(loadFieldStats).toHaveBeenCalledTimes(1); + + expect(wrapper.text()).toBe( + 'Toggle either theTop valuesDistribution1273.9%1326.1%100% of 23 documents' + ); + }); +}); diff --git a/src/plugins/unified_field_list/public/components/field_stats/field_stats.tsx b/src/plugins/unified_field_list/public/components/field_stats/field_stats.tsx new file mode 100755 index 0000000000000..590451a255894 --- /dev/null +++ b/src/plugins/unified_field_list/public/components/field_stats/field_stats.tsx @@ -0,0 +1,595 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { + DataView, + DataViewField, + ES_FIELD_TYPES, + getEsQueryConfig, + KBN_FIELD_TYPES, +} from '@kbn/data-plugin/common'; +import type { IUiSettingsClient } from '@kbn/core/public'; +import type { DataViewsContract } from '@kbn/data-views-plugin/public'; +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +import type { ChartsPluginSetup } from '@kbn/charts-plugin/public'; +import DateMath from '@kbn/datemath'; +import { + EuiButtonGroup, + EuiFlexGroup, + EuiFlexItem, + EuiLoadingSpinner, + EuiProgress, + EuiSpacer, + EuiText, + EuiTitle, + EuiToolTip, + useEuiTheme, +} from '@elastic/eui'; +import { css } from '@emotion/react'; +import { + Axis, + Chart, + HistogramBarSeries, + niceTimeFormatter, + Position, + ScaleType, + Settings, + TooltipType, +} from '@elastic/charts'; +import { i18n } from '@kbn/i18n'; +import { buildEsQuery, Query, Filter, AggregateQuery } from '@kbn/es-query'; +import type { BucketedAggregation } from '../../../common/types'; +import { canProvideStatsForField } from '../../../common/utils/field_stats_utils'; +import { loadFieldStats } from '../../services/field_stats'; + +interface State { + isLoading: boolean; + totalDocuments?: number; + sampledDocuments?: number; + sampledValues?: number; + histogram?: BucketedAggregation; + topValues?: BucketedAggregation; +} + +export interface FieldStatsServices { + uiSettings: IUiSettingsClient; + dataViews: DataViewsContract; + data: DataPublicPluginStart; + fieldFormats: FieldFormatsStart; + charts: ChartsPluginSetup; +} + +export interface FieldStatsProps { + services: FieldStatsServices; + query: Query | AggregateQuery; + filters: Filter[]; + fromDate: string; + toDate: string; + dataViewOrDataViewId: DataView | string; + field: DataViewField; + 'data-test-subj'?: string; + overrideMissingContent?: (params?: { noDataFound?: boolean }) => JSX.Element | null; + overrideFooter?: (params: { + element: JSX.Element; + totalDocuments?: number; + sampledDocuments?: number; + }) => JSX.Element; +} + +const FieldStatsComponent: React.FC = ({ + services, + query, + filters, + fromDate, + toDate, + dataViewOrDataViewId, + field, + 'data-test-subj': dataTestSubject = 'fieldStats', + overrideMissingContent, + overrideFooter, +}) => { + const { euiTheme } = useEuiTheme(); + const { fieldFormats, uiSettings, charts, dataViews, data } = services; + const [state, changeState] = useState({ + isLoading: false, + }); + const [dataView, changeDataView] = useState(null); + const abortControllerRef = useRef(null); + const isCanceledRef = useRef(false); + + const topValueStyles = useMemo( + () => css` + margin-bottom: ${euiTheme.size.s}; + + &:last-of-type { + margin-bottom: 0; + } + `, + [euiTheme] + ); + + const topValueProgressStyles = useMemo( + () => css` + background-color: ${euiTheme.colors.lightestShade}; + + &::-webkit-progress-bar { + background-color: ${euiTheme.colors.lightestShade}; + } + `, + [euiTheme] + ); + + const setState: typeof changeState = useCallback( + (nextState) => { + if (!isCanceledRef.current) { + changeState(nextState); + } + }, + [changeState, isCanceledRef] + ); + + const setDataView: typeof changeDataView = useCallback( + (nextDataView) => { + if (!isCanceledRef.current) { + changeDataView(nextDataView); + } + }, + [changeDataView, isCanceledRef] + ); + + async function fetchData() { + if (isCanceledRef.current) { + return; + } + + try { + const loadedDataView = + typeof dataViewOrDataViewId === 'string' + ? await dataViews.get(dataViewOrDataViewId) + : dataViewOrDataViewId; + + setDataView(loadedDataView); + + if (state.isLoading || !canProvideStatsForField(field)) { + return; + } + + setState((s) => ({ ...s, isLoading: true })); + + abortControllerRef.current?.abort(); + abortControllerRef.current = new AbortController(); + + const results = await loadFieldStats({ + services: { data }, + dataView: loadedDataView, + field, + fromDate, + toDate, + dslQuery: buildEsQuery(loadedDataView, query, filters, getEsQueryConfig(uiSettings)), + abortController: abortControllerRef.current, + }); + + abortControllerRef.current = null; + + setState((s) => ({ + ...s, + isLoading: false, + totalDocuments: results.totalDocuments, + sampledDocuments: results.sampledDocuments, + sampledValues: results.sampledValues, + histogram: results.histogram, + topValues: results.topValues, + })); + } catch (e) { + // console.error(e); + setState((s) => ({ ...s, isLoading: false })); + } + } + + useEffect(() => { + fetchData(); + + return () => { + isCanceledRef.current = true; + abortControllerRef.current?.abort(); + }; + }, []); // eslint-disable-line react-hooks/exhaustive-deps + + const chartTheme = charts.theme.useChartsTheme(); + const chartBaseTheme = charts.theme.useChartsBaseTheme(); + + const { isLoading, histogram, topValues, sampledValues, sampledDocuments, totalDocuments } = + state; + + let histogramDefault = !!state.histogram; + const fromDateParsed = DateMath.parse(fromDate); + const toDateParsed = DateMath.parse(toDate); + + const totalValuesCount = + topValues && topValues.buckets.reduce((prev, bucket) => bucket.count + prev, 0); + const otherCount = sampledValues && totalValuesCount ? sampledValues - totalValuesCount : 0; + + if ( + totalValuesCount && + histogram && + histogram.buckets.length && + topValues && + topValues.buckets.length + ) { + // Default to histogram when top values are less than 10% of total + histogramDefault = otherCount / totalValuesCount > 0.9; + } + + const [showingHistogram, setShowingHistogram] = useState(histogramDefault); + + if (isLoading) { + return ; + } + + if (!dataView) { + return null; + } + + const formatter = dataView.getFormatterForField(field); + let title = <>; + + if (field.type.includes('range')) { + return ( + <> + + {i18n.translate('unifiedFieldList.fieldStats.notAvailableForRangeFieldDescription', { + defaultMessage: `Summary information is not available for range type fields.`, + })} + + + ); + } + + if (field.type === 'murmur3') { + return ( + <> + + {i18n.translate('unifiedFieldList.fieldStats.notAvailableForMurmur3FieldDescription', { + defaultMessage: `Summary information is not available for murmur3 fields.`, + })} + + + ); + } + + if (field.type === 'geo_point' || field.type === 'geo_shape') { + return overrideMissingContent ? overrideMissingContent() : null; + } + + if ( + (!histogram || histogram.buckets.length === 0) && + (!topValues || topValues.buckets.length === 0) + ) { + return overrideMissingContent ? overrideMissingContent({ noDataFound: true }) : null; + } + + if (histogram && histogram.buckets.length && topValues && topValues.buckets.length) { + title = ( + { + setShowingHistogram(optionId === 'histogram'); + }} + idSelected={showingHistogram ? 'histogram' : 'topValues'} + /> + ); + } else if (field.type === 'date') { + title = ( + +
+ {i18n.translate('unifiedFieldList.fieldStats.fieldTimeDistributionLabel', { + defaultMessage: 'Time distribution', + })} +
+
+ ); + } else if (topValues && topValues.buckets.length) { + title = ( + +
+ {i18n.translate('unifiedFieldList.fieldStats.topValuesLabel', { + defaultMessage: 'Top values', + })} +
+
+ ); + } + + function combineWithTitleAndFooter(el: React.ReactElement) { + const countsElement = totalDocuments ? ( + + {sampledDocuments && ( + <> + {i18n.translate('unifiedFieldList.fieldStats.percentageOfLabel', { + defaultMessage: '{percentage}% of', + values: { + percentage: Math.round((sampledDocuments / totalDocuments) * 100), + }, + })}{' '} + + )} + + {fieldFormats + .getDefaultInstance(KBN_FIELD_TYPES.NUMBER, [ES_FIELD_TYPES.INTEGER]) + .convert(totalDocuments)} + {' '} + {i18n.translate('unifiedFieldList.fieldStats.ofDocumentsLabel', { + defaultMessage: 'documents', + })} + + ) : ( + <> + ); + + return ( + <> + {title ? title : <>} + + + + {el} + + {overrideFooter ? ( + overrideFooter?.({ element: countsElement, totalDocuments, sampledDocuments }) + ) : ( + <> + + {countsElement} + + )} + + ); + } + + if (histogram && histogram.buckets.length) { + const specId = i18n.translate('unifiedFieldList.fieldStats.countLabel', { + defaultMessage: 'Count', + }); + + if (field.type === 'date') { + return combineWithTitleAndFooter( + + + + + + + + ); + } + + if (showingHistogram || !topValues || !topValues.buckets.length) { + return combineWithTitleAndFooter( + + + + formatter.convert(d)} + /> + + + + ); + } + } + + if (topValues && topValues.buckets.length) { + const digitsRequired = topValues.buckets.some( + (topValue) => !Number.isInteger(topValue.count / sampledValues!) + ); + return combineWithTitleAndFooter( +
+ {topValues.buckets.map((topValue) => { + const formatted = formatter.convert(topValue.key); + return ( +
+ + + {formatted === '' ? ( + + + {i18n.translate('unifiedFieldList.fieldStats.emptyStringValueLabel', { + defaultMessage: 'Empty string', + })} + + + ) : ( + + + {formatted} + + + )} + + + + {(Math.round((topValue.count / sampledValues!) * 1000) / 10).toFixed( + digitsRequired ? 1 : 0 + )} + % + + + + +
+ ); + })} + {otherCount ? ( + <> + + + + {i18n.translate('unifiedFieldList.fieldStats.otherDocsLabel', { + defaultMessage: 'Other', + })} + + + + + + {(Math.round((otherCount / sampledValues!) * 1000) / 10).toFixed( + digitsRequired ? 1 : 0 + )} + % + + + + + + + ) : ( + <> + )} +
+ ); + } + + return null; +}; + +class ErrorBoundary extends React.Component<{}, { hasError: boolean }> { + constructor(props: FieldStatsProps) { + super(props); + this.state = { hasError: false }; + } + + static getDerivedStateFromError() { + return { hasError: true }; + } + + // componentDidCatch(error, errorInfo) { + // console.log(error, errorInfo); + // } + + render() { + if (this.state.hasError) { + return null; + } + + return this.props.children; + } +} + +/** + * Component which fetches and renders stats for a data view field + * @param props + * @constructor + */ +const FieldStats: React.FC = (props) => { + return ( + + + + ); +}; + +// Necessary for React.lazy +// eslint-disable-next-line import/no-default-export +export default FieldStats; diff --git a/src/plugins/unified_field_list/public/components/field_stats/index.tsx b/src/plugins/unified_field_list/public/components/field_stats/index.tsx new file mode 100755 index 0000000000000..0cfb29d4abd5f --- /dev/null +++ b/src/plugins/unified_field_list/public/components/field_stats/index.tsx @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { Fragment } from 'react'; +import type { FieldStatsProps, FieldStatsServices } from './field_stats'; + +const Fallback = () => ; + +const LazyFieldStats = React.lazy(() => import('./field_stats')); +const WrappedFieldStats: React.FC = (props) => ( + }> + + +); + +export const FieldStats = WrappedFieldStats; +export type { FieldStatsProps, FieldStatsServices }; diff --git a/src/plugins/unified_field_list/public/index.ts b/src/plugins/unified_field_list/public/index.ts new file mode 100755 index 0000000000000..f2f666dd47481 --- /dev/null +++ b/src/plugins/unified_field_list/public/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { UnifiedFieldListPlugin } from './plugin'; + +export type { + FieldStatsResponse, + BucketedAggregation, + NumberStatsResult, + TopValuesResult, +} from '../common/types'; +export type { FieldStatsProps, FieldStatsServices } from './components/field_stats'; +export { FieldStats } from './components/field_stats'; +export { loadFieldStats } from './services/field_stats'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. +export function plugin() { + return new UnifiedFieldListPlugin(); +} +export type { UnifiedFieldListPluginSetup, UnifiedFieldListPluginStart } from './types'; diff --git a/src/plugins/unified_field_list/public/plugin.ts b/src/plugins/unified_field_list/public/plugin.ts new file mode 100755 index 0000000000000..009b73dd1c575 --- /dev/null +++ b/src/plugins/unified_field_list/public/plugin.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; +import { UnifiedFieldListPluginSetup, UnifiedFieldListPluginStart } from './types'; + +export class UnifiedFieldListPlugin + implements Plugin +{ + public setup(core: CoreSetup): UnifiedFieldListPluginSetup { + // Return methods that should be available to other plugins + return {}; + } + + public start(core: CoreStart): UnifiedFieldListPluginStart { + return {}; + } + + public stop() {} +} diff --git a/src/plugins/unified_field_list/public/services/field_stats/index.tsx b/src/plugins/unified_field_list/public/services/field_stats/index.tsx new file mode 100755 index 0000000000000..644bca5a054d4 --- /dev/null +++ b/src/plugins/unified_field_list/public/services/field_stats/index.tsx @@ -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 type { LoadFieldStatsHandler } from './load_field_stats'; + +export const loadFieldStats: LoadFieldStatsHandler = async (params) => { + const { loadFieldStats: loadFieldStatsHandler } = await import('./load_field_stats'); + return await loadFieldStatsHandler(params); +}; diff --git a/src/plugins/unified_field_list/public/services/field_stats/load_field_stats.ts b/src/plugins/unified_field_list/public/services/field_stats/load_field_stats.ts new file mode 100644 index 0000000000000..fd38cd0bfca64 --- /dev/null +++ b/src/plugins/unified_field_list/public/services/field_stats/load_field_stats.ts @@ -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 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 { lastValueFrom } from 'rxjs'; +import type { DataView } from '@kbn/data-views-plugin/common'; +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { DataViewFieldBase } from '@kbn/es-query'; +import type { FieldStatsResponse } from '../../../common/types'; +import { + fetchAndCalculateFieldStats, + SearchHandler, + buildSearchParams, +} from '../../../common/utils/field_stats_utils'; + +interface FetchFieldStatsParams { + services: { + data: DataPublicPluginStart; + }; + dataView: DataView; + field: DataViewFieldBase; + fromDate: string; + toDate: string; + dslQuery: object; + size?: number; + abortController?: AbortController; +} + +export type LoadFieldStatsHandler = ( + params: FetchFieldStatsParams +) => Promise>; + +/** + * Loads and aggregates stats data for a data view field + * @param services + * @param dataView + * @param field + * @param fromDate + * @param toDate + * @param dslQuery + * @param size + * @param abortController + */ +export const loadFieldStats: LoadFieldStatsHandler = async ({ + services, + dataView, + field, + fromDate, + toDate, + dslQuery, + size, + abortController, +}) => { + const { data } = services; + + try { + if (!dataView?.id || !field?.type) { + return {}; + } + + const searchHandler: SearchHandler = async (aggs) => { + const result = await lastValueFrom( + data.search.search( + { + params: buildSearchParams({ + dataViewPattern: dataView.title, + timeFieldName: dataView.timeFieldName, + fromDate, + toDate, + dslQuery, + runtimeMappings: dataView.getRuntimeMappings(), + aggs, + }), + }, + { + abortSignal: abortController?.signal, + } + ) + ); + return result.rawResponse; + }; + + return await fetchAndCalculateFieldStats({ + searchHandler, + field, + fromDate, + toDate, + size, + }); + } catch (error) { + // console.error(error); + throw new Error('Could not provide field stats', { cause: error }); + } +}; diff --git a/src/plugins/unified_field_list/public/types.ts b/src/plugins/unified_field_list/public/types.ts new file mode 100755 index 0000000000000..feb24509cdee5 --- /dev/null +++ b/src/plugins/unified_field_list/public/types.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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. + */ + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface UnifiedFieldListPluginSetup {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface UnifiedFieldListPluginStart {} diff --git a/src/plugins/unified_field_list/server/index.ts b/src/plugins/unified_field_list/server/index.ts new file mode 100644 index 0000000000000..039ea0488b533 --- /dev/null +++ b/src/plugins/unified_field_list/server/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { PluginInitializerContext } from '@kbn/core/server'; +import { UnifiedFieldListPlugin } from './plugin'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. + +export function plugin(initializerContext: PluginInitializerContext) { + return new UnifiedFieldListPlugin(initializerContext); +} + +export type { + UnifiedFieldListServerPluginSetup, + UnifiedFieldListServerPluginStart, + PluginSetup, + PluginStart, +} from './types'; diff --git a/src/plugins/unified_field_list/server/plugin.ts b/src/plugins/unified_field_list/server/plugin.ts new file mode 100644 index 0000000000000..2853afcf3d6fd --- /dev/null +++ b/src/plugins/unified_field_list/server/plugin.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { PluginInitializerContext, CoreSetup, CoreStart, Plugin, Logger } from '@kbn/core/server'; +import { + UnifiedFieldListServerPluginSetup, + UnifiedFieldListServerPluginStart, + PluginStart, + PluginSetup, +} from './types'; +import { defineRoutes } from './routes'; + +export class UnifiedFieldListPlugin + implements Plugin +{ + private readonly logger: Logger; + + constructor(initializerContext: PluginInitializerContext) { + this.logger = initializerContext.logger.get(); + } + + public setup(core: CoreSetup, plugins: PluginSetup) { + this.logger.debug('unifiedFieldList: Setup'); + + defineRoutes(core); + + return {}; + } + + public start(core: CoreStart, plugins: PluginStart) { + this.logger.debug('unifiedFieldList: Started'); + return {}; + } + + public stop() {} +} diff --git a/src/plugins/unified_field_list/server/routes/field_stats.ts b/src/plugins/unified_field_list/server/routes/field_stats.ts new file mode 100644 index 0000000000000..4fc654f892210 --- /dev/null +++ b/src/plugins/unified_field_list/server/routes/field_stats.ts @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { errors } from '@elastic/elasticsearch'; +import { schema } from '@kbn/config-schema'; +import { CoreSetup } from '@kbn/core/server'; +import { SavedObjectNotFound } from '@kbn/kibana-utils-plugin/common'; +import { FIELD_STATS_API_PATH } from '../../common/constants'; +import type { PluginStart } from '../types'; +import { + fetchAndCalculateFieldStats, + SearchHandler, + buildSearchParams, +} from '../../common/utils/field_stats_utils'; + +export async function initFieldStatsRoute(setup: CoreSetup) { + const router = setup.http.createRouter(); + router.post( + { + path: FIELD_STATS_API_PATH, + validate: { + body: schema.object( + { + dslQuery: schema.object({}, { unknowns: 'allow' }), + fromDate: schema.string(), + toDate: schema.string(), + dataViewId: schema.string(), + fieldName: schema.string(), + size: schema.maybe(schema.number()), + }, + { unknowns: 'allow' } + ), + }, + }, + async (context, req, res) => { + const requestClient = (await context.core).elasticsearch.client.asCurrentUser; + const { fromDate, toDate, fieldName, dslQuery, size, dataViewId } = req.body; + + const [{ savedObjects, elasticsearch }, { dataViews }] = await setup.getStartServices(); + const savedObjectsClient = savedObjects.getScopedClient(req); + const esClient = elasticsearch.client.asScoped(req).asCurrentUser; + const indexPatternsService = await dataViews.dataViewsServiceFactory( + savedObjectsClient, + esClient + ); + + try { + const dataView = await indexPatternsService.get(dataViewId); + const field = dataView.fields.find((f) => f.name === fieldName); + + if (!field) { + throw new Error(`Field {fieldName} not found in data view ${dataView.title}`); + } + + const searchHandler: SearchHandler = async (aggs) => { + const result = await requestClient.search( + buildSearchParams({ + dataViewPattern: dataView.title, + timeFieldName: dataView.timeFieldName, + fromDate, + toDate, + dslQuery, + runtimeMappings: dataView.getRuntimeMappings(), + aggs, + }) + ); + return result; + }; + + const stats = await fetchAndCalculateFieldStats({ + searchHandler, + field, + fromDate, + toDate, + size, + }); + + return res.ok({ + body: stats, + }); + } catch (e) { + if (e instanceof SavedObjectNotFound) { + return res.notFound(); + } + if (e instanceof errors.ResponseError && e.statusCode === 404) { + return res.notFound(); + } + if (e.isBoom) { + if (e.output.statusCode === 404) { + return res.notFound(); + } + throw new Error(e.output.message); + } else { + throw e; + } + } + } + ); +} diff --git a/src/plugins/unified_field_list/server/routes/index.ts b/src/plugins/unified_field_list/server/routes/index.ts new file mode 100644 index 0000000000000..3558e93e4a780 --- /dev/null +++ b/src/plugins/unified_field_list/server/routes/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { CoreSetup } from '@kbn/core/server'; +import { PluginStart } from '../types'; +import { initFieldStatsRoute } from './field_stats'; + +export function defineRoutes(setup: CoreSetup) { + initFieldStatsRoute(setup); +} diff --git a/src/plugins/unified_field_list/server/types.ts b/src/plugins/unified_field_list/server/types.ts new file mode 100644 index 0000000000000..56cd69a01881e --- /dev/null +++ b/src/plugins/unified_field_list/server/types.ts @@ -0,0 +1,22 @@ +/* + * Copyright 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 { PluginStart as DataViewsServerPluginStart } from '@kbn/data-views-plugin/server'; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface UnifiedFieldListServerPluginSetup {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface UnifiedFieldListServerPluginStart {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface PluginSetup {} + +export interface PluginStart { + dataViews: DataViewsServerPluginStart; +} diff --git a/src/plugins/unified_field_list/tsconfig.json b/src/plugins/unified_field_list/tsconfig.json new file mode 100644 index 0000000000000..221729fbd2b71 --- /dev/null +++ b/src/plugins/unified_field_list/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "../../typings/**/*", + "common/**/*", + "public/**/*", + "server/**/*", + ], + "references": [ + { "path": "../../core/tsconfig.json" }, + { "path": "../kibana_utils/tsconfig.json" }, + { "path": "../kibana_react/tsconfig.json" }, + { "path": "../data_views/tsconfig.json" }, + { "path": "../data/tsconfig.json" }, + { "path": "../charts/tsconfig.json" } + ] +} diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/index.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/index.test.ts index c0ce9d5f8804e..435335fe9dd25 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/index.test.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/index.test.ts @@ -5,33 +5,9 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import type { DataView } from '@kbn/data-plugin/common'; -import type { Panel, Series } from '../../common/types'; -import { convertTSVBtoLensConfiguration } from '.'; - -const dataViewsMap: Record = { - test1: { id: 'test1', title: 'test1', timeFieldName: 'timeField1' } as DataView, - test2: { - id: 'test2', - title: 'test2', - timeFieldName: 'timeField2', - } as DataView, - test3: { id: 'test3', title: 'test3', timeFieldName: 'timeField3' } as DataView, -}; -const getDataview = (id: string): DataView | undefined => dataViewsMap[id]; -jest.mock('../services', () => { - return { - getDataViewsStart: jest.fn(() => { - return { - getDefault: jest.fn(() => { - return { id: '12345', title: 'default', timeFieldName: '@timestamp' }; - }), - get: getDataview, - }; - }), - }; -}); +import type { Panel } from '../../common/types'; +import { convertTSVBtoLensConfiguration } from '.'; const model = { axis_position: 'left', @@ -61,7 +37,7 @@ const model = { } as Panel; describe('convertTSVBtoLensConfiguration', () => { - test('should return null for a non timeseries chart', async () => { + test('should return null for a not supported chart', async () => { const metricModel = { ...model, type: 'metric', @@ -78,279 +54,4 @@ describe('convertTSVBtoLensConfiguration', () => { const triggerOptions = await convertTSVBtoLensConfiguration(stringIndexPatternModel); expect(triggerOptions).toBeNull(); }); - - test('should return null for a non supported aggregation', async () => { - const nonSupportedAggModel = { - ...model, - series: [ - { - ...model.series[0], - metrics: [ - { - type: 'std_deviation', - }, - ] as Series['metrics'], - }, - ], - }; - const triggerOptions = await convertTSVBtoLensConfiguration(nonSupportedAggModel); - expect(triggerOptions).toBeNull(); - }); - - test('should return options for a supported aggregation', async () => { - const triggerOptions = await convertTSVBtoLensConfiguration(model); - expect(triggerOptions).toStrictEqual({ - configuration: { - extents: { yLeftExtent: { mode: 'full' }, yRightExtent: { mode: 'full' } }, - fill: '0', - gridLinesVisibility: { x: false, yLeft: false, yRight: false }, - legend: { - isVisible: false, - maxLines: 1, - position: 'right', - shouldTruncate: false, - showSingleSeries: false, - }, - }, - type: 'lnsXY', - layers: { - '0': { - axisPosition: 'left', - chartType: 'line', - collapseFn: undefined, - indexPatternId: 'test2', - metrics: [ - { - agg: 'count', - color: '#000000', - fieldName: 'document', - isFullReference: false, - params: {}, - }, - ], - palette: { - name: 'default', - type: 'palette', - }, - splitWithDateHistogram: false, - timeFieldName: 'timeField2', - timeInterval: 'auto', - dropPartialBuckets: false, - }, - }, - }); - }); - - test('should return area for timeseries line chart with fill > 0', async () => { - const modelWithFill = { - ...model, - series: [ - { - ...model.series[0], - fill: '0.3', - stacked: 'none', - }, - ], - }; - const triggerOptions = await convertTSVBtoLensConfiguration(modelWithFill); - expect(triggerOptions?.layers[0].chartType).toBe('area'); - }); - - test('should return timeShift in the params if it is provided', async () => { - const modelWithFill = { - ...model, - series: [ - { - ...model.series[0], - offset_time: '1h', - }, - ], - }; - const triggerOptions = await convertTSVBtoLensConfiguration(modelWithFill); - expect(triggerOptions?.layers[0]?.metrics?.[0]?.params?.shift).toBe('1h'); - }); - - test('should return filter in the params if it is provided', async () => { - const modelWithFill = { - ...model, - series: [ - { - ...model.series[0], - filter: { - language: 'kuery', - query: 'test', - }, - }, - ], - }; - const triggerOptions = await convertTSVBtoLensConfiguration(modelWithFill); - expect(triggerOptions?.layers[0]?.metrics?.[0]?.params?.kql).toBe('test'); - }); - - test('should return splitFilters information if the chart is broken down by filters', async () => { - const modelWithSplitFilters = { - ...model, - series: [ - { - ...model.series[0], - split_mode: 'filters', - split_filters: [ - { - color: 'rgba(188,0,85,1)', - filter: { - language: 'kuery', - query: '', - }, - id: '89afac60-7d2b-11ec-917c-c18cd38d60b5', - }, - ], - }, - ], - }; - const triggerOptions = await convertTSVBtoLensConfiguration(modelWithSplitFilters); - expect(triggerOptions?.layers[0]?.splitFilters).toStrictEqual([ - { - color: 'rgba(188,0,85,1)', - filter: { - language: 'kuery', - query: '', - }, - id: '89afac60-7d2b-11ec-917c-c18cd38d60b5', - }, - ]); - }); - - test('should return termsParams information if the chart is broken down by terms including series agg collapse fn', async () => { - const modelWithTerms = { - ...model, - series: [ - { - ...model.series[0], - metrics: [ - ...model.series[0].metrics, - { - type: 'series_agg', - function: 'sum', - }, - ], - split_mode: 'terms', - terms_size: 6, - terms_direction: 'desc', - terms_order_by: '_key', - }, - ] as unknown as Series[], - }; - const triggerOptions = await convertTSVBtoLensConfiguration(modelWithTerms); - expect(triggerOptions?.layers[0]?.collapseFn).toStrictEqual('sum'); - expect(triggerOptions?.layers[0]?.termsParams).toStrictEqual({ - size: 6, - otherBucket: false, - orderDirection: 'desc', - orderBy: { type: 'alphabetical' }, - includeIsRegex: false, - excludeIsRegex: false, - parentFormat: { - id: 'terms', - }, - }); - }); - - test('should return include exclude information if the chart is broken down by terms', async () => { - const modelWithTerms = { - ...model, - series: [ - { - ...model.series[0], - split_mode: 'terms', - terms_size: 6, - terms_direction: 'desc', - terms_order_by: '_key', - terms_include: 't.*', - }, - ] as unknown as Series[], - }; - const triggerOptions = await convertTSVBtoLensConfiguration(modelWithTerms); - expect(triggerOptions?.layers[0]?.termsParams).toStrictEqual({ - size: 6, - otherBucket: false, - orderDirection: 'desc', - orderBy: { type: 'alphabetical' }, - includeIsRegex: true, - include: ['t.*'], - excludeIsRegex: false, - parentFormat: { - id: 'terms', - }, - }); - }); - - test('should return custom time interval if it is given', async () => { - const modelWithTerms = { - ...model, - interval: '1h', - }; - const triggerOptions = await convertTSVBtoLensConfiguration(modelWithTerms); - expect(triggerOptions?.layers[0]?.timeInterval).toBe('1h'); - }); - - test('should return dropPartialbuckets if enabled', async () => { - const modelWithDropBuckets = { - ...model, - drop_last_bucket: 1, - }; - const triggerOptions = await convertTSVBtoLensConfiguration(modelWithDropBuckets); - expect(triggerOptions?.layers[0]?.dropPartialBuckets).toBe(true); - }); - - test('should return the correct chart configuration', async () => { - const modelWithConfig = { - ...model, - show_legend: 1, - legend_position: 'bottom', - truncate_legend: 0, - show_grid: 1, - series: [{ ...model.series[0], fill: '0.3', separate_axis: 1, axis_position: 'right' }], - }; - const triggerOptions = await convertTSVBtoLensConfiguration(modelWithConfig); - expect(triggerOptions).toStrictEqual({ - configuration: { - extents: { yLeftExtent: { mode: 'full' }, yRightExtent: { mode: 'full' } }, - fill: '0.3', - gridLinesVisibility: { x: true, yLeft: true, yRight: true }, - legend: { - isVisible: true, - maxLines: 1, - position: 'bottom', - shouldTruncate: false, - showSingleSeries: true, - }, - }, - type: 'lnsXY', - layers: { - '0': { - axisPosition: 'right', - chartType: 'area_stacked', - collapseFn: undefined, - indexPatternId: 'test2', - metrics: [ - { - agg: 'count', - color: '#000000', - fieldName: 'document', - isFullReference: false, - params: {}, - }, - ], - palette: { - name: 'default', - type: 'palette', - }, - splitWithDateHistogram: false, - timeFieldName: 'timeField2', - timeInterval: 'auto', - dropPartialBuckets: false, - }, - }, - }); - }); }); diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/index.ts index 071001381f0f1..08806f42e1976 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/index.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/index.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { TimeRange } from '@kbn/data-plugin/common'; import type { Panel } from '../../common/types'; import { PANEL_TYPES } from '../../common/enums'; import { ConvertTsvbToLensVisualization } from './types'; @@ -18,6 +19,10 @@ const getConvertFnByType = ( const { convertToLens } = await import('./timeseries'); return convertToLens; }, + [PANEL_TYPES.TOP_N]: async () => { + const { convertToLens } = await import('./top_n'); + return convertToLens; + }, }; return convertionFns[type]?.(); @@ -28,12 +33,17 @@ const getConvertFnByType = ( * Returns the Lens model, only if it is supported. If not, it returns null. * In case of null, the menu item is disabled and the user can't navigate to Lens. */ -export const convertTSVBtoLensConfiguration = async (model: Panel) => { - // Disables the option for not timeseries charts, for the string mode and for series with annotations +export const convertTSVBtoLensConfiguration = async (model: Panel, timeRange?: TimeRange) => { + // Disables the option for not supported charts, for the string mode and for series with annotations if (!model.use_kibana_indexes || (model.annotations && model.annotations.length > 0)) { return null; } + // Disables if model is invalid + if (model.isModelInvalid) { + return null; + } + const convertFn = await getConvertFnByType(model.type); - return (await convertFn?.(model)) ?? null; + return (await convertFn?.(model, timeRange)) ?? null; }; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/layers/get_layer.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/layers/get_layer.ts index 8483c6f2c3191..de4c61e54837e 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/layers/get_layer.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/layers/get_layer.ts @@ -41,18 +41,21 @@ export const getLayerConfiguration = ( model: Panel, series: VisSeries, splitFields: string[], - timeField?: string, - splitWithDateHistogram?: boolean + xFieldName?: string, + xMode?: string, + splitWithDateHistogram?: boolean, + window?: string ): VisualizeEditorLayersContext => { const layer = model.series[layerIdx]; const palette = layer.palette as PaletteOutput; const splitFilters = convertSplitFilters(layer); const { metrics: metricsArray, seriesAgg } = series; const filter = convertFilter(layer); - const metrics = convertMetrics(layer, metricsArray, filter); + const metrics = convertMetrics(layer, metricsArray, filter, window); return { indexPatternId, - timeFieldName: timeField, + xFieldName, + xMode, chartType, axisPosition: layer.separate_axis ? layer.axis_position : model.axis_position, ...(layer.terms_field && { splitFields }), diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/filter_ratio_formula.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/filter_ratio_formula.ts index a132b861889fa..c341351c66418 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/filter_ratio_formula.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/filter_ratio_formula.ts @@ -7,6 +7,7 @@ */ import type { Query } from '@kbn/es-query'; +import { addTimeRangeToFormula } from '.'; import type { Metric } from '../../../../common/types'; import { SUPPORTED_METRICS } from './supported_metrics'; @@ -14,15 +15,15 @@ const escapeQuotes = (str: string) => { return str?.replace(/'/g, "\\'"); }; -const constructFilterRationFormula = (operation: string, metric?: Query) => { +const constructFilterRationFormula = (operation: string, metric?: Query, window?: string) => { return `${operation}${metric?.language === 'lucene' ? 'lucene' : 'kql'}='${ metric?.query && typeof metric?.query === 'string' ? escapeQuotes(metric?.query) : metric?.query ?? '*' - }')`; + }'${addTimeRangeToFormula(window)})`; }; -export const getFilterRatioFormula = (currentMetric: Metric) => { +export const getFilterRatioFormula = (currentMetric: Metric, window?: string) => { // eslint-disable-next-line @typescript-eslint/naming-convention const { numerator, denominator, metric_agg, field } = currentMetric; let aggregation = SUPPORTED_METRICS.count; @@ -38,16 +39,18 @@ export const getFilterRatioFormula = (currentMetric: Metric) => { if (aggregation.name === 'counter_rate') { const numeratorFormula = constructFilterRationFormula( `${aggregation.name}(max('${field}',`, - numerator + numerator, + window ); const denominatorFormula = constructFilterRationFormula( `${aggregation.name}(max('${field}',`, - denominator + denominator, + window ); return `${numeratorFormula}) / ${denominatorFormula})`; } else { - const numeratorFormula = constructFilterRationFormula(operation, numerator); - const denominatorFormula = constructFilterRationFormula(operation, denominator); + const numeratorFormula = constructFilterRationFormula(operation, numerator, window); + const denominatorFormula = constructFilterRationFormula(operation, denominator, window); return `${numeratorFormula} / ${denominatorFormula}`; } }; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/index.ts index 9a79fb804e3ba..08a7599ba3f8d 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/index.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/index.ts @@ -13,3 +13,4 @@ export * from './parent_pipeline_formula'; export * from './sibling_pipeline_formula'; export * from './filter_ratio_formula'; export * from './parent_pipeline_series'; +export * from './validate_metrics'; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/metrics_converter.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/metrics_converter.ts index 65427bfa2a268..6ea305fdface3 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/metrics_converter.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/metrics_converter.ts @@ -27,7 +27,8 @@ export const convertFilter = (series: Series): Filter | void => { const convertMetric = ( series: Series, metric: VisualizeEditorLayersContext['metrics'][number], - filter: Filter | void + filter: Filter | void, + interval?: string ) => ({ ...metric, color: metric.color ?? series.color, @@ -35,11 +36,13 @@ const convertMetric = ( ...metric.params, ...(series.offset_time && { shift: series.offset_time }), ...(filter && filter), + ...(interval && { window: interval }), }, }); export const convertMetrics = ( series: Series, metrics: VisualizeEditorLayersContext['metrics'], - filter: Filter | void -) => metrics.map((metric) => convertMetric(series, metric, filter)); + filter: Filter | void, + interval?: string +) => metrics.map((metric) => convertMetric(series, metric, filter, interval)); diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/metrics_helpers.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/metrics_helpers.test.ts index 83922a54aa111..b07bcecb7f790 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/metrics_helpers.test.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/metrics_helpers.test.ts @@ -44,7 +44,7 @@ describe('getPercentilesSeries', () => { value: '90', }, ] as Metric['percentiles']; - const config = getPercentilesSeries(percentiles, 'bytes'); + const config = getPercentilesSeries(percentiles, 'everything', '', 'bytes'); expect(config).toStrictEqual([ { agg: 'percentile', @@ -82,7 +82,7 @@ describe('getPercentileRankSeries', () => { test('should return correct config for multiple percentile ranks', () => { const values = ['1', '5', '7'] as Metric['values']; const colors = ['#68BC00', 'rgba(0,63,188,1)', 'rgba(188,38,0,1)'] as Metric['colors']; - const config = getPercentileRankSeries(values, colors, 'day_of_week_i'); + const config = getPercentileRankSeries(values, colors, 'everything', '', 'day_of_week_i'); expect(config).toStrictEqual([ { agg: 'percentile_rank', diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/metrics_helpers.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/metrics_helpers.ts index fdc7f4ca2f6d0..400adb4571fb7 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/metrics_helpers.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/metrics_helpers.ts @@ -6,18 +6,28 @@ * Side Public License, v 1. */ +import { utc } from 'moment'; +import { search } from '@kbn/data-plugin/public'; +import dateMath from '@kbn/datemath'; +import { TimeRange, UI_SETTINGS } from '@kbn/data-plugin/common'; +import { getUISettings } from '../../../services'; import type { Metric } from '../../../../common/types'; import { SUPPORTED_METRICS } from './supported_metrics'; import { getFilterRatioFormula } from './filter_ratio_formula'; import { getParentPipelineSeriesFormula } from './parent_pipeline_formula'; import { getSiblingPipelineSeriesFormula } from './sibling_pipeline_formula'; -export const getPercentilesSeries = (percentiles: Metric['percentiles'], fieldName?: string) => { +export const getPercentilesSeries = ( + percentiles: Metric['percentiles'], + splitMode: string, + layerColor: string, + fieldName?: string +) => { return percentiles?.map((percentile) => { return { agg: 'percentile', isFullReference: false, - color: percentile.color, + color: splitMode === 'everything' ? percentile.color : layerColor, fieldName: fieldName ?? 'document', params: { percentile: percentile.value }, }; @@ -27,19 +37,42 @@ export const getPercentilesSeries = (percentiles: Metric['percentiles'], fieldNa export const getPercentileRankSeries = ( values: Metric['values'], colors: Metric['colors'], + splitMode: string, + layerColor: string, fieldName?: string ) => { return values?.map((value, index) => { return { agg: 'percentile_rank', isFullReference: false, - color: colors?.[index], + color: splitMode === 'everything' ? colors?.[index] : layerColor, fieldName: fieldName ?? 'document', params: { value }, }; }); }; +export const getWindow = (interval?: string, timeRange?: TimeRange) => { + let window = interval || '1h'; + + if (timeRange && !interval) { + const { from, to } = timeRange; + const timerange = utc(to).valueOf() - utc(from).valueOf(); + const maxBars = getUISettings().get(UI_SETTINGS.HISTOGRAM_BAR_TARGET); + + const duration = search.aggs.calcAutoIntervalLessThan(maxBars, timerange); + const unit = + dateMath.units.find((u) => { + const value = duration.as(u); + return Number.isInteger(value); + }) || 'ms'; + + window = `${duration.as(unit)}${unit}`; + } + + return window; +}; + export const getTimeScale = (metric: Metric) => { const supportedTimeScales = ['1s', '1m', '1h', '1d']; let timeScale; @@ -60,6 +93,10 @@ export const getFormulaSeries = (script: string) => { ]; }; +export const addTimeRangeToFormula = (window?: string) => { + return window ? `, timeRange='${window}'` : ''; +}; + export const getPipelineAgg = (subFunctionMetric: Metric) => { const pipelineAggMap = SUPPORTED_METRICS[subFunctionMetric.type]; if (!pipelineAggMap) { @@ -71,7 +108,8 @@ export const getPipelineAgg = (subFunctionMetric: Metric) => { export const getFormulaEquivalent = ( currentMetric: Metric, metrics: Metric[], - metaValue?: number + metaValue?: number, + window?: string ) => { const aggregation = SUPPORTED_METRICS[currentMetric.type]?.name; switch (currentMetric.type) { @@ -80,7 +118,7 @@ export const getFormulaEquivalent = ( case 'min_bucket': case 'sum_bucket': case 'positive_only': { - return getSiblingPipelineSeriesFormula(currentMetric.type, currentMetric, metrics); + return getSiblingPipelineSeriesFormula(currentMetric.type, currentMetric, metrics, window); } case 'count': { return `${aggregation}()`; @@ -88,10 +126,12 @@ export const getFormulaEquivalent = ( case 'percentile': { return `${aggregation}(${currentMetric.field}${ metaValue ? `, percentile=${metaValue}` : '' - })`; + }${addTimeRangeToFormula(window)})`; } case 'percentile_rank': { - return `${aggregation}(${currentMetric.field}${metaValue ? `, value=${metaValue}` : ''})`; + return `${aggregation}(${currentMetric.field}${ + metaValue ? `, value=${metaValue}` : '' + }${addTimeRangeToFormula(window)})`; } case 'cumulative_sum': case 'derivative': @@ -110,20 +150,34 @@ export const getFormulaEquivalent = ( subFunctionMetric, pipelineAgg, currentMetric.type, - metaValue + metaValue, + window ); } case 'positive_rate': { - return `${aggregation}(max(${currentMetric.field}))`; + return `${aggregation}(max(${currentMetric.field}${addTimeRangeToFormula(window)}))`; } case 'filter_ratio': { - return getFilterRatioFormula(currentMetric); + return getFilterRatioFormula(currentMetric, window); } case 'static': { return `${currentMetric.value}`; } - default: { + case 'std_deviation': { + if (currentMetric.mode === 'lower') { + return `average(${currentMetric.field}${addTimeRangeToFormula(window)}) - ${ + currentMetric.sigma || 1.5 + } * ${aggregation}(${currentMetric.field}${addTimeRangeToFormula(window)})`; + } + if (currentMetric.mode === 'upper') { + return `average(${currentMetric.field}${addTimeRangeToFormula(window)}) + ${ + currentMetric.sigma || 1.5 + } * ${aggregation}(${currentMetric.field}${addTimeRangeToFormula(window)})`; + } return `${aggregation}(${currentMetric.field})`; } + default: { + return `${aggregation}(${currentMetric.field}${addTimeRangeToFormula(window)})`; + } } }; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/parent_pipeline_formula.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/parent_pipeline_formula.ts index e00fe505df1fd..7d12e77fdb8bc 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/parent_pipeline_formula.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/parent_pipeline_formula.ts @@ -8,14 +8,15 @@ import type { Metric, MetricType } from '../../../../common/types'; import { SUPPORTED_METRICS } from './supported_metrics'; -import { getFilterRatioFormula } from './filter_ratio_formula'; +import { getFormulaEquivalent } from './metrics_helpers'; export const getParentPipelineSeriesFormula = ( metrics: Metric[], subFunctionMetric: Metric, pipelineAgg: string, aggregation: MetricType, - percentileValue?: number + percentileValue?: number, + window?: string ) => { let formula = ''; const aggregationMap = SUPPORTED_METRICS[aggregation]; @@ -42,28 +43,13 @@ export const getParentPipelineSeriesFormula = ( additionalSubFunction.field ?? '' }${additionalFunctionArgs ?? ''})))`; } else { - let additionalFunctionArgs; - if (pipelineAgg === 'percentile' && percentileValue) { - additionalFunctionArgs = `, percentile=${percentileValue}`; - } - if (pipelineAgg === 'percentile_rank' && percentileValue) { - additionalFunctionArgs = `, value=${percentileValue}`; - } - if (pipelineAgg === 'filter_ratio') { - const script = getFilterRatioFormula(subFunctionMetric); - if (!script) { - return null; - } - formula = `${aggregationMap.name}(${script}${additionalFunctionArgs ?? ''})`; - } else if (pipelineAgg === 'counter_rate') { - formula = `${aggregationMap.name}(${pipelineAgg}(max(${subFunctionMetric.field}${ - additionalFunctionArgs ? `${additionalFunctionArgs}` : '' - })))`; - } else { - formula = `${aggregationMap.name}(${pipelineAgg}(${subFunctionMetric.field}${ - additionalFunctionArgs ? `${additionalFunctionArgs}` : '' - }))`; + const subFormula = getFormulaEquivalent(subFunctionMetric, metrics, percentileValue, window); + + if (!subFormula) { + return null; } + + formula = `${aggregationMap.name}(${subFormula})`; } return formula; }; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/parent_pipeline_series.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/parent_pipeline_series.test.ts index 40264b89491b7..db8faccf5976f 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/parent_pipeline_series.test.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/parent_pipeline_series.test.ts @@ -91,7 +91,7 @@ describe('getParentPipelineSeries', () => { { field: 'AvgTicketPrice', id: '04558549-f19f-4a87-9923-27df8b81af3e', - type: 'std_deviation', + type: 'sum_of_squares_bucket', }, { field: '04558549-f19f-4a87-9923-27df8b81af3e', diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/parent_pipeline_series.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/parent_pipeline_series.ts index 6a263ca8bb44d..6f0caf3837b5c 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/parent_pipeline_series.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/parent_pipeline_series.ts @@ -17,11 +17,12 @@ export const computeParentSeries = ( currentMetric: Metric, subFunctionMetric: Metric, pipelineAgg: string, - meta?: number + meta?: number, + window?: string ) => { const aggregationMap = SUPPORTED_METRICS[aggregation]; if (subFunctionMetric.type === 'filter_ratio') { - const script = getFilterRatioFormula(subFunctionMetric); + const script = getFilterRatioFormula(subFunctionMetric, window); if (!script) { return null; } @@ -49,7 +50,8 @@ export const computeParentSeries = ( export const getParentPipelineSeries = ( aggregation: MetricType, currentMetricIdx: number, - metrics: Metric[] + metrics: Metric[], + window?: string ) => { const currentMetric = metrics[currentMetricIdx]; // percentile value is derived from the field Id. It has the format xxx-xxx-xxx-xxx[percentile] @@ -73,7 +75,8 @@ export const getParentPipelineSeries = ( subFunctionMetric, pipelineAgg, aggregation, - metaValue + metaValue, + window ); if (!formula) { return null; @@ -85,7 +88,8 @@ export const getParentPipelineSeries = ( currentMetric, subFunctionMetric, pipelineAgg, - metaValue + metaValue, + window ); } }; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/sibling_pipeline_formula.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/sibling_pipeline_formula.ts index b3e141b11e598..4243d6c0dd27c 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/sibling_pipeline_formula.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/sibling_pipeline_formula.ts @@ -7,12 +7,14 @@ */ import type { Metric, MetricType } from '../../../../common/types'; +import { getFormulaEquivalent } from './metrics_helpers'; import { SUPPORTED_METRICS } from './supported_metrics'; export const getSiblingPipelineSeriesFormula = ( aggregation: MetricType, currentMetric: Metric, - metrics: Metric[] + metrics: Metric[], + window?: string ) => { const [nestedFieldId, nestedMeta] = currentMetric.field?.split('[') ?? []; const subFunctionMetric = metrics.find((metric) => metric.id === nestedFieldId); @@ -43,18 +45,15 @@ export const getSiblingPipelineSeriesFormula = ( additionalSubFunctionField ?? '' }))${minimumValue})`; } else { - let additionalFunctionArgs; - // handle percentile and percentile_rank const nestedMetaValue = Number(nestedMeta?.replace(']', '')); - if (pipelineAggMap.name === 'percentile' && nestedMetaValue) { - additionalFunctionArgs = `, percentile=${nestedMetaValue}`; - } - if (pipelineAggMap.name === 'percentile_rank' && nestedMetaValue) { - additionalFunctionArgs = `, value=${nestedMetaValue}`; + + const subFormula = getFormulaEquivalent(subFunctionMetric, metrics, nestedMetaValue, window); + + if (!subFormula) { + return null; } - formula += `${pipelineAggMap.name}(${subMetricField ?? ''}${ - additionalFunctionArgs ? `${additionalFunctionArgs}` : '' - })${minimumValue})`; + + formula += `${subFormula}${minimumValue})`; } return formula; }; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/supported_metrics.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/supported_metrics.ts index 29bf2008e208d..a5a50f5f032cf 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/supported_metrics.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/supported_metrics.ts @@ -6,9 +6,13 @@ * Side Public License, v 1. */ +import { PANEL_TYPES, TIME_RANGE_DATA_MODES } from '../../../../common/enums'; interface AggOptions { name: string; isFullReference: boolean; + isFieldRequired: boolean; + supportedPanelTypes: string[]; + supportedTimeRangeModes: string[]; } // list of supported TSVB aggregation types in Lens @@ -19,85 +23,207 @@ export const SUPPORTED_METRICS: { [key: string]: AggOptions } = { avg: { name: 'average', isFullReference: false, + isFieldRequired: true, + supportedPanelTypes: [PANEL_TYPES.TIMESERIES, PANEL_TYPES.TOP_N], + supportedTimeRangeModes: [ + TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE, + TIME_RANGE_DATA_MODES.LAST_VALUE, + ], }, cardinality: { name: 'unique_count', isFullReference: false, + isFieldRequired: true, + supportedPanelTypes: [PANEL_TYPES.TIMESERIES, PANEL_TYPES.TOP_N], + supportedTimeRangeModes: [ + TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE, + TIME_RANGE_DATA_MODES.LAST_VALUE, + ], }, count: { name: 'count', isFullReference: false, + isFieldRequired: false, + supportedPanelTypes: [PANEL_TYPES.TIMESERIES, PANEL_TYPES.TOP_N], + supportedTimeRangeModes: [ + TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE, + TIME_RANGE_DATA_MODES.LAST_VALUE, + ], }, positive_rate: { name: 'counter_rate', isFullReference: true, + isFieldRequired: true, + supportedPanelTypes: [PANEL_TYPES.TIMESERIES], + supportedTimeRangeModes: [ + TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE, + TIME_RANGE_DATA_MODES.LAST_VALUE, + ], }, moving_average: { name: 'moving_average', isFullReference: true, + isFieldRequired: true, + supportedPanelTypes: [PANEL_TYPES.TIMESERIES], + supportedTimeRangeModes: [TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE], }, derivative: { name: 'differences', isFullReference: true, + isFieldRequired: true, + supportedPanelTypes: [PANEL_TYPES.TIMESERIES], + supportedTimeRangeModes: [TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE], }, cumulative_sum: { name: 'cumulative_sum', isFullReference: true, + isFieldRequired: true, + supportedPanelTypes: [PANEL_TYPES.TIMESERIES], + supportedTimeRangeModes: [TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE], }, avg_bucket: { name: 'overall_average', isFullReference: true, + isFieldRequired: true, + supportedPanelTypes: [PANEL_TYPES.TIMESERIES], + supportedTimeRangeModes: [TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE], }, max_bucket: { name: 'overall_max', isFullReference: true, + isFieldRequired: true, + supportedPanelTypes: [PANEL_TYPES.TIMESERIES], + supportedTimeRangeModes: [TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE], }, min_bucket: { name: 'overall_min', isFullReference: true, + isFieldRequired: true, + supportedPanelTypes: [PANEL_TYPES.TIMESERIES], + supportedTimeRangeModes: [TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE], }, sum_bucket: { name: 'overall_sum', isFullReference: true, + isFieldRequired: true, + supportedPanelTypes: [PANEL_TYPES.TIMESERIES], + supportedTimeRangeModes: [TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE], }, max: { name: 'max', isFullReference: false, + isFieldRequired: true, + supportedPanelTypes: [PANEL_TYPES.TIMESERIES, PANEL_TYPES.TOP_N], + supportedTimeRangeModes: [ + TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE, + TIME_RANGE_DATA_MODES.LAST_VALUE, + ], }, min: { name: 'min', isFullReference: false, + isFieldRequired: true, + supportedPanelTypes: [PANEL_TYPES.TIMESERIES, PANEL_TYPES.TOP_N], + supportedTimeRangeModes: [ + TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE, + TIME_RANGE_DATA_MODES.LAST_VALUE, + ], }, percentile: { name: 'percentile', isFullReference: false, + isFieldRequired: true, + supportedPanelTypes: [PANEL_TYPES.TIMESERIES, PANEL_TYPES.TOP_N], + supportedTimeRangeModes: [ + TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE, + TIME_RANGE_DATA_MODES.LAST_VALUE, + ], }, percentile_rank: { name: 'percentile_rank', isFullReference: false, + isFieldRequired: true, + supportedPanelTypes: [PANEL_TYPES.TIMESERIES, PANEL_TYPES.TOP_N], + supportedTimeRangeModes: [ + TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE, + TIME_RANGE_DATA_MODES.LAST_VALUE, + ], }, sum: { name: 'sum', isFullReference: false, + isFieldRequired: true, + supportedPanelTypes: [PANEL_TYPES.TIMESERIES, PANEL_TYPES.TOP_N], + supportedTimeRangeModes: [ + TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE, + TIME_RANGE_DATA_MODES.LAST_VALUE, + ], }, filter_ratio: { name: 'filter_ratio', isFullReference: false, + isFieldRequired: false, + supportedPanelTypes: [PANEL_TYPES.TIMESERIES, PANEL_TYPES.TOP_N], + supportedTimeRangeModes: [ + TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE, + TIME_RANGE_DATA_MODES.LAST_VALUE, + ], }, top_hit: { name: 'last_value', isFullReference: false, + isFieldRequired: true, + supportedPanelTypes: [PANEL_TYPES.TIMESERIES, PANEL_TYPES.TOP_N], + supportedTimeRangeModes: [ + TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE, + TIME_RANGE_DATA_MODES.LAST_VALUE, + ], }, math: { name: 'formula', isFullReference: true, + isFieldRequired: false, + supportedPanelTypes: [PANEL_TYPES.TIMESERIES, PANEL_TYPES.TOP_N], + supportedTimeRangeModes: [ + TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE, + TIME_RANGE_DATA_MODES.LAST_VALUE, + ], }, positive_only: { name: 'pick_max', isFullReference: true, + isFieldRequired: true, + supportedPanelTypes: [PANEL_TYPES.TIMESERIES], + supportedTimeRangeModes: [TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE], }, static: { name: 'static_value', isFullReference: true, + isFieldRequired: false, + supportedPanelTypes: [PANEL_TYPES.TIMESERIES, PANEL_TYPES.TOP_N], + supportedTimeRangeModes: [ + TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE, + TIME_RANGE_DATA_MODES.LAST_VALUE, + ], + }, + value_count: { + name: 'count', + isFullReference: false, + isFieldRequired: true, + supportedPanelTypes: [PANEL_TYPES.TIMESERIES, PANEL_TYPES.TOP_N], + supportedTimeRangeModes: [ + TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE, + TIME_RANGE_DATA_MODES.LAST_VALUE, + ], + }, + std_deviation: { + name: 'standard_deviation', + isFullReference: false, + isFieldRequired: true, + supportedPanelTypes: [PANEL_TYPES.TIMESERIES, PANEL_TYPES.TOP_N], + supportedTimeRangeModes: [ + TIME_RANGE_DATA_MODES.ENTIRE_TIME_RANGE, + TIME_RANGE_DATA_MODES.LAST_VALUE, + ], }, }; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/validate_metrics.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/validate_metrics.ts new file mode 100644 index 0000000000000..adcc2e9a94664 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/metrics/validate_metrics.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { Metric } from '../../../../common/types'; +import { SUPPORTED_METRICS } from '.'; + +const isMetricValid = ( + metricType: string, + panelType: string, + field?: string, + timeRangeMode?: string +) => { + const isMetricSupported = SUPPORTED_METRICS[metricType]; + if (!isMetricSupported) { + return false; + } + const isPanelTypeSupported = + SUPPORTED_METRICS[metricType].supportedPanelTypes.includes(panelType); + const isTimeRangeModeSupported = + !timeRangeMode || SUPPORTED_METRICS[metricType].supportedTimeRangeModes.includes(timeRangeMode); + return ( + isPanelTypeSupported && + isTimeRangeModeSupported && + (!SUPPORTED_METRICS[metricType].isFieldRequired || field) + ); +}; + +export const isValidMetrics = (metrics: Metric[], panelType: string, timeRangeMode?: string) => { + return metrics.every((metric) => { + const isMetricAggValid = + metric.type !== 'filter_ratio' || + isMetricValid(metric.metric_agg || 'count', panelType, metric.field, timeRangeMode); + return ( + metric.type === 'series_agg' || + (isMetricValid(metric.type, panelType, metric.field, timeRangeMode) && isMetricAggValid) + ); + }); +}; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/get_series.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/get_series.test.ts index aeb401e86dd01..80b7ba29b24c7 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/get_series.test.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/get_series.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 type { Metric } from '../../../../common/types'; import { getSeries } from './get_series'; @@ -17,7 +18,7 @@ describe('getSeries', () => { field: 'day_of_week_i', }, ] as Metric[]; - const config = getSeries(metric, 1)!.metrics; + const config = getSeries(metric, 1, 'everything', '')!.metrics; expect(config).toStrictEqual([ { agg: 'average', @@ -44,7 +45,7 @@ describe('getSeries', () => { }, }, ] as Metric[]; - const config = getSeries(metric, 1)!.metrics; + const config = getSeries(metric, 1, 'everything', '')!.metrics; expect(config).toStrictEqual([ { agg: 'formula', @@ -71,7 +72,7 @@ describe('getSeries', () => { field: '123456', }, ] as Metric[]; - const config = getSeries(metric, 1)!.metrics; + const config = getSeries(metric, 1, 'everything', '')!.metrics; expect(config).toStrictEqual([ { agg: 'formula', @@ -97,7 +98,7 @@ describe('getSeries', () => { field: '123456', }, ] as Metric[]; - const config = getSeries(metric, 1)!.metrics; + const config = getSeries(metric, 1, 'everything', '')!.metrics; expect(config).toStrictEqual([ { agg: 'formula', @@ -122,7 +123,7 @@ describe('getSeries', () => { field: '123456', }, ] as Metric[]; - const config = getSeries(metric, 1)!.metrics; + const config = getSeries(metric, 1, 'everything', '')!.metrics; expect(config).toStrictEqual([ { agg: 'cumulative_sum', @@ -147,7 +148,7 @@ describe('getSeries', () => { field: '123456', }, ] as Metric[]; - const config = getSeries(metric, 1)!.metrics; + const config = getSeries(metric, 1, 'everything', '')!.metrics; expect(config).toStrictEqual([ { agg: 'formula', @@ -174,7 +175,7 @@ describe('getSeries', () => { unit: '1m', }, ] as Metric[]; - const config = getSeries(metric, 1)!.metrics; + const config = getSeries(metric, 1, 'everything', '')!.metrics; expect(config).toStrictEqual([ { agg: 'differences', @@ -202,7 +203,7 @@ describe('getSeries', () => { window: 6, }, ] as Metric[]; - const config = getSeries(metric, 1)!.metrics; + const config = getSeries(metric, 1, 'everything', '')!.metrics; expect(config).toStrictEqual([ { agg: 'moving_average', @@ -246,7 +247,7 @@ describe('getSeries', () => { window: 6, }, ] as Metric[]; - const config = getSeries(metric, 1)!.metrics; + const config = getSeries(metric, 1, 'everything', '')!.metrics; expect(config).toStrictEqual([ { agg: 'formula', @@ -293,7 +294,7 @@ describe('getSeries', () => { ], }, ] as Metric[]; - const config = getSeries(metric, 1)!.metrics; + const config = getSeries(metric, 1, 'everything', '')!.metrics; expect(config).toStrictEqual([ { agg: 'percentile', @@ -335,7 +336,7 @@ describe('getSeries', () => { colors: ['rgba(211,96,134,1)', 'rgba(155,33,230,1)', '#68BC00'], }, ] as Metric[]; - const config = getSeries(metric, 1)!.metrics; + const config = getSeries(metric, 1, 'everything', '')!.metrics; expect(config).toStrictEqual([ { agg: 'percentile_rank', @@ -377,7 +378,7 @@ describe('getSeries', () => { order_by: 'timestamp', }, ] as Metric[]; - const config = getSeries(metric, 1)!.metrics; + const config = getSeries(metric, 1, 'everything', '')!.metrics; expect(config).toStrictEqual([ { agg: 'last_value', @@ -405,7 +406,7 @@ describe('getSeries', () => { function: 'mean', }, ] as Metric[]; - const config = getSeries(metric, 1)!; + const config = getSeries(metric, 1, 'everything', '')!; expect(config).toStrictEqual({ metrics: [ { @@ -430,7 +431,7 @@ describe('getSeries', () => { size: 2, }, ] as Metric[]; - const config = getSeries(metric, 1); + const config = getSeries(metric, 1, 'everything', ''); expect(config).toBeNull(); }); @@ -442,7 +443,7 @@ describe('getSeries', () => { value: '10', }, ] as Metric[]; - const config = getSeries(metric, 1); + const config = getSeries(metric, 1, 'everything', ''); expect(config).toBeNull(); }); @@ -454,7 +455,7 @@ describe('getSeries', () => { value: '10', }, ] as Metric[]; - const config = getSeries(metric, 2)!.metrics; + const config = getSeries(metric, 2, 'everything', '')!.metrics; expect(config).toStrictEqual([ { agg: 'static_value', @@ -521,7 +522,7 @@ describe('getSeries', () => { ], }, ] as Metric[]; - const config = getSeries(metric, 1)!.metrics; + const config = getSeries(metric, 1, 'everything', '')!.metrics; expect(config).toStrictEqual([ { agg: 'formula', @@ -572,7 +573,7 @@ describe('getSeries', () => { ], }, ] as Metric[]; - const config = getSeries(metric, 1)!.metrics; + const config = getSeries(metric, 1, 'everything', '')!.metrics; expect(config).toStrictEqual([ { agg: 'formula', diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/get_series.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/get_series.ts index 0db270b465719..a5c4bbfbc548d 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/get_series.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/series/get_series.ts @@ -28,7 +28,13 @@ export interface VisSeries { seriesAgg?: string; } -export const getSeries = (initialMetrics: Metric[], totalSeriesNum: number): VisSeries | null => { +export const getSeries = ( + initialMetrics: Metric[], + totalSeriesNum: number, + splitMode: string, + layerColor: string, + window?: string +): VisSeries | null => { const { metrics, seriesAgg } = getSeriesAgg(initialMetrics); const metricIdx = metrics.length - 1; const aggregation = metrics[metricIdx].type; @@ -44,6 +50,8 @@ export const getSeries = (initialMetrics: Metric[], totalSeriesNum: number): Vis if (percentiles?.length) { const percentilesSeries = getPercentilesSeries( percentiles, + splitMode, + layerColor, fieldName ) as VisualizeEditorLayersContext['metrics']; metricsArray = [...metricsArray, ...percentilesSeries]; @@ -57,6 +65,8 @@ export const getSeries = (initialMetrics: Metric[], totalSeriesNum: number): Vis const percentileRanksSeries = getPercentileRankSeries( values, colors, + splitMode, + layerColor, fieldName ) as VisualizeEditorLayersContext['metrics']; metricsArray = [...metricsArray, ...percentileRanksSeries]; @@ -92,12 +102,17 @@ export const getSeries = (initialMetrics: Metric[], totalSeriesNum: number): Vis const [_, meta] = variable?.field?.split('[') ?? []; const metaValue = Number(meta?.replace(']', '')); if (!metaValue) return; - const script = getFormulaEquivalent(currentMetric, layerMetricsArray, metaValue); + const script = getFormulaEquivalent( + currentMetric, + layerMetricsArray, + metaValue, + window + ); if (!script) return; finalScript = finalScript?.replace(`params.${variable.name}`, script); }); } else { - const script = getFormulaEquivalent(currentMetric, layerMetricsArray); + const script = getFormulaEquivalent(currentMetric, layerMetricsArray, undefined, window); if (!script) return null; const variable = variables.find((v) => v.field === currentMetric.id); finalScript = finalScript?.replaceAll(`params.${variable?.name}`, script); @@ -113,7 +128,8 @@ export const getSeries = (initialMetrics: Metric[], totalSeriesNum: number): Vis metricsArray = getParentPipelineSeries( aggregation, metricIdx, - metrics + metrics, + window ) as VisualizeEditorLayersContext['metrics']; break; } @@ -137,7 +153,8 @@ export const getSeries = (initialMetrics: Metric[], totalSeriesNum: number): Vis subFunctionMetric, pipelineAgg, aggregation, - metaValue + metaValue, + window ); if (!formula) return null; metricsArray = getFormulaSeries(formula); @@ -146,7 +163,9 @@ export const getSeries = (initialMetrics: Metric[], totalSeriesNum: number): Vis aggregation, metrics[metricIdx], subFunctionMetric, - pipelineAgg + pipelineAgg, + undefined, + window ); if (!series) return null; metricsArray = series; @@ -154,7 +173,12 @@ export const getSeries = (initialMetrics: Metric[], totalSeriesNum: number): Vis break; } case 'positive_only': { - const formula = getSiblingPipelineSeriesFormula(aggregation, metrics[metricIdx], metrics); + const formula = getSiblingPipelineSeriesFormula( + aggregation, + metrics[metricIdx], + metrics, + window + ); if (!formula) { return null; } @@ -165,7 +189,12 @@ export const getSeries = (initialMetrics: Metric[], totalSeriesNum: number): Vis case 'max_bucket': case 'min_bucket': case 'sum_bucket': { - const formula = getSiblingPipelineSeriesFormula(aggregation, metrics[metricIdx], metrics); + const formula = getSiblingPipelineSeriesFormula( + aggregation, + metrics[metricIdx], + metrics, + window + ); if (!formula) { return null; } @@ -173,7 +202,7 @@ export const getSeries = (initialMetrics: Metric[], totalSeriesNum: number): Vis break; } case 'filter_ratio': { - const formula = getFilterRatioFormula(metrics[metricIdx]); + const formula = getFilterRatioFormula(metrics[metricIdx], window); if (!formula) { return null; } @@ -221,6 +250,25 @@ export const getSeries = (initialMetrics: Metric[], totalSeriesNum: number): Vis ]; break; } + case 'std_deviation': { + const currentMetric = metrics[metricIdx]; + if (currentMetric.mode === 'upper' || currentMetric.mode === 'lower') { + const script = getFormulaEquivalent(currentMetric, metrics, undefined, window); + if (!script) return null; + metricsArray = getFormulaSeries(script); + break; + } else if (currentMetric.mode === 'band') { + [ + { ...currentMetric, mode: 'upper' }, + { ...currentMetric, mode: 'lower' }, + ].forEach((metric) => { + const script = getFormulaEquivalent(metric, metrics, undefined, window); + if (!script) return null; + metricsArray.push(...getFormulaSeries(script)); + }); + break; + } + } default: { const timeScale = getTimeScale(metrics[metricIdx]); metricsArray = [ diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.test.ts new file mode 100644 index 0000000000000..7593018a22593 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.test.ts @@ -0,0 +1,340 @@ +/* + * Copyright 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 { DataView } from '@kbn/data-plugin/common'; +import type { Panel, Series } from '../../../common/types'; +import { convertToLens } from '.'; + +const dataViewsMap: Record = { + test1: { id: 'test1', title: 'test1', timeFieldName: 'timeField1' } as DataView, + test2: { + id: 'test2', + title: 'test2', + timeFieldName: 'timeField2', + } as DataView, + test3: { id: 'test3', title: 'test3', timeFieldName: 'timeField3' } as DataView, +}; + +const getDataview = (id: string): DataView | undefined => dataViewsMap[id]; +jest.mock('../../services', () => { + return { + getDataViewsStart: jest.fn(() => { + return { + getDefault: jest.fn(() => { + return { id: '12345', title: 'default', timeFieldName: '@timestamp' }; + }), + get: getDataview, + }; + }), + }; +}); + +const model = { + axis_position: 'left', + type: 'timeseries', + index_pattern: { id: 'test2' }, + use_kibana_indexes: true, + series: [ + { + color: '#000000', + chart_type: 'line', + fill: '0', + id: '85147356-c185-4636-9182-d55f3ab2b6fa', + palette: { + name: 'default', + type: 'palette', + }, + split_mode: 'everything', + metrics: [ + { + id: '3fa8b32f-5c38-4813-9361-1f2817ae5b18', + type: 'count', + }, + ], + override_index_pattern: 0, + }, + ], +} as Panel; + +describe('convertToLens for TimeSeries', () => { + test('should return null for a non supported aggregation', async () => { + const nonSupportedAggModel = { + ...model, + series: [ + { + ...model.series[0], + metrics: [ + { + type: 'sum_of_squares_bucket', + }, + ] as Series['metrics'], + }, + ], + }; + const triggerOptions = await convertToLens(nonSupportedAggModel); + expect(triggerOptions).toBeNull(); + }); + + test('should return options for a supported aggregation', async () => { + const triggerOptions = await convertToLens(model); + expect(triggerOptions).toStrictEqual({ + configuration: { + extents: { yLeftExtent: { mode: 'full' }, yRightExtent: { mode: 'full' } }, + fill: '0', + gridLinesVisibility: { x: false, yLeft: false, yRight: false }, + legend: { + isVisible: false, + maxLines: 1, + position: 'right', + shouldTruncate: false, + showSingleSeries: false, + }, + }, + type: 'lnsXY', + layers: { + '0': { + axisPosition: 'left', + chartType: 'line', + collapseFn: undefined, + indexPatternId: 'test2', + metrics: [ + { + agg: 'count', + color: '#000000', + fieldName: 'document', + isFullReference: false, + params: {}, + }, + ], + palette: { + name: 'default', + type: 'palette', + }, + splitWithDateHistogram: false, + xFieldName: 'timeField2', + xMode: 'date_histogram', + timeInterval: 'auto', + dropPartialBuckets: false, + }, + }, + }); + }); + + test('should return area for timeseries line chart with fill > 0', async () => { + const modelWithFill = { + ...model, + series: [ + { + ...model.series[0], + fill: '0.3', + stacked: 'none', + }, + ], + }; + const triggerOptions = await convertToLens(modelWithFill); + expect(triggerOptions?.layers[0].chartType).toBe('area'); + }); + + test('should return timeShift in the params if it is provided', async () => { + const modelWithFill = { + ...model, + series: [ + { + ...model.series[0], + offset_time: '1h', + }, + ], + }; + const triggerOptions = await convertToLens(modelWithFill); + expect(triggerOptions?.layers[0]?.metrics?.[0]?.params?.shift).toBe('1h'); + }); + + test('should return filter in the params if it is provided', async () => { + const modelWithFill = { + ...model, + series: [ + { + ...model.series[0], + filter: { + language: 'kuery', + query: 'test', + }, + }, + ], + }; + const triggerOptions = await convertToLens(modelWithFill); + expect(triggerOptions?.layers[0]?.metrics?.[0]?.params?.kql).toBe('test'); + }); + + test('should return splitFilters information if the chart is broken down by filters', async () => { + const modelWithSplitFilters = { + ...model, + series: [ + { + ...model.series[0], + split_mode: 'filters', + split_filters: [ + { + color: 'rgba(188,0,85,1)', + filter: { + language: 'kuery', + query: '', + }, + id: '89afac60-7d2b-11ec-917c-c18cd38d60b5', + }, + ], + }, + ], + }; + const triggerOptions = await convertToLens(modelWithSplitFilters); + expect(triggerOptions?.layers[0]?.splitFilters).toStrictEqual([ + { + color: 'rgba(188,0,85,1)', + filter: { + language: 'kuery', + query: '', + }, + id: '89afac60-7d2b-11ec-917c-c18cd38d60b5', + }, + ]); + }); + + test('should return termsParams information if the chart is broken down by terms including series agg collapse fn', async () => { + const modelWithTerms = { + ...model, + series: [ + { + ...model.series[0], + metrics: [ + ...model.series[0].metrics, + { + type: 'series_agg', + function: 'sum', + }, + ], + split_mode: 'terms', + terms_size: 6, + terms_direction: 'desc', + terms_order_by: '_key', + }, + ] as unknown as Series[], + }; + const triggerOptions = await convertToLens(modelWithTerms); + expect(triggerOptions?.layers[0]?.collapseFn).toStrictEqual('sum'); + expect(triggerOptions?.layers[0]?.termsParams).toStrictEqual({ + size: 6, + otherBucket: false, + orderDirection: 'desc', + orderBy: { type: 'alphabetical' }, + includeIsRegex: false, + excludeIsRegex: false, + parentFormat: { + id: 'terms', + }, + }); + }); + + test('should return include exclude information if the chart is broken down by terms', async () => { + const modelWithTerms = { + ...model, + series: [ + { + ...model.series[0], + split_mode: 'terms', + terms_size: 6, + terms_direction: 'desc', + terms_order_by: '_key', + terms_include: 't.*', + }, + ] as unknown as Series[], + }; + const triggerOptions = await convertToLens(modelWithTerms); + expect(triggerOptions?.layers[0]?.termsParams).toStrictEqual({ + size: 6, + otherBucket: false, + orderDirection: 'desc', + orderBy: { type: 'alphabetical' }, + includeIsRegex: true, + include: ['t.*'], + excludeIsRegex: false, + parentFormat: { + id: 'terms', + }, + }); + }); + + test('should return custom time interval if it is given', async () => { + const modelWithTerms = { + ...model, + interval: '1h', + }; + const triggerOptions = await convertToLens(modelWithTerms); + expect(triggerOptions?.layers[0]?.timeInterval).toBe('1h'); + }); + + test('should return dropPartialbuckets if enabled', async () => { + const modelWithDropBuckets = { + ...model, + drop_last_bucket: 1, + }; + const triggerOptions = await convertToLens(modelWithDropBuckets); + expect(triggerOptions?.layers[0]?.dropPartialBuckets).toBe(true); + }); + + test('should return the correct chart configuration', async () => { + const modelWithConfig = { + ...model, + show_legend: 1, + legend_position: 'bottom', + truncate_legend: 0, + show_grid: 1, + series: [{ ...model.series[0], fill: '0.3', separate_axis: 1, axis_position: 'right' }], + }; + const triggerOptions = await convertToLens(modelWithConfig); + expect(triggerOptions).toStrictEqual({ + configuration: { + extents: { yLeftExtent: { mode: 'full' }, yRightExtent: { mode: 'full' } }, + fill: '0.3', + gridLinesVisibility: { x: true, yLeft: true, yRight: true }, + legend: { + isVisible: true, + maxLines: 1, + position: 'bottom', + shouldTruncate: false, + showSingleSeries: true, + }, + }, + type: 'lnsXY', + layers: { + '0': { + axisPosition: 'right', + chartType: 'area_stacked', + collapseFn: undefined, + indexPatternId: 'test2', + metrics: [ + { + agg: 'count', + color: '#000000', + fieldName: 'document', + isFullReference: false, + params: {}, + }, + ], + palette: { + name: 'default', + type: 'palette', + }, + splitWithDateHistogram: false, + xFieldName: 'timeField2', + xMode: 'date_histogram', + timeInterval: 'auto', + dropPartialBuckets: false, + }, + }, + }); + }); +}); diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.ts index 2996ad19c42d9..1aa81b950a28c 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/timeseries/index.ts @@ -7,6 +7,7 @@ */ import { VisualizeEditorLayersContext } from '@kbn/visualizations-plugin/public'; +import { PANEL_TYPES } from '../../../common/enums'; import { getDataViewsStart } from '../../services'; import { getDataSourceInfo } from '../lib/datasource'; import { getSeries } from '../lib/series'; @@ -15,6 +16,7 @@ import { ConvertTsvbToLensVisualization } from '../types'; import { convertChartType, getYExtents } from '../lib/xy'; import { getLayerConfiguration } from '../lib/layers'; import { isSplitWithDateHistogram } from '../lib/split_chart'; +import { isValidMetrics } from '../lib/metrics'; export const convertToLens: ConvertTsvbToLensVisualization = async (model) => { const layersConfiguration: { [key: string]: VisualizeEditorLayersContext } = {}; @@ -30,6 +32,10 @@ export const convertToLens: ConvertTsvbToLensVisualization = async (model) => { continue; } + if (!isValidMetrics(layer.metrics, PANEL_TYPES.TIMESERIES)) { + return null; + } + const { indexPatternId, timeField } = await getDataSourceInfo( model.index_pattern, model.time_field, @@ -39,7 +45,7 @@ export const convertToLens: ConvertTsvbToLensVisualization = async (model) => { ); // handle multiple metrics - const series = getSeries(layer.metrics, seriesNum); + const series = getSeries(layer.metrics, seriesNum, layer.split_mode, layer.color); if (!series || !series.metrics) { return null; } @@ -68,6 +74,7 @@ export const convertToLens: ConvertTsvbToLensVisualization = async (model) => { series, splitFields, timeField, + 'date_histogram', splitWithDateHistogram ); } diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/top_n/index.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/top_n/index.test.ts new file mode 100644 index 0000000000000..1f408bf3dfa89 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/top_n/index.test.ts @@ -0,0 +1,329 @@ +/* + * Copyright 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 { DataView } from '@kbn/data-plugin/common'; +import type { Panel, Series } from '../../../common/types'; +import { convertToLens } from '.'; + +const dataViewsMap: Record = { + test1: { id: 'test1', title: 'test1', timeFieldName: 'timeField1' } as DataView, + test2: { + id: 'test2', + title: 'test2', + timeFieldName: 'timeField2', + } as DataView, + test3: { id: 'test3', title: 'test3', timeFieldName: 'timeField3' } as DataView, +}; + +const getDataview = (id: string): DataView | undefined => dataViewsMap[id]; +jest.mock('../../services', () => { + return { + getDataViewsStart: jest.fn(() => { + return { + getDefault: jest.fn(() => { + return { id: '12345', title: 'default', timeFieldName: '@timestamp' }; + }), + get: getDataview, + }; + }), + }; +}); + +const model = { + axis_position: 'left', + type: 'timeseries', + index_pattern: { id: 'test2' }, + use_kibana_indexes: true, + series: [ + { + color: '#000000', + chart_type: 'line', + fill: '0', + id: '85147356-c185-4636-9182-d55f3ab2b6fa', + palette: { + name: 'default', + type: 'palette', + }, + split_mode: 'everything', + metrics: [ + { + id: '3fa8b32f-5c38-4813-9361-1f2817ae5b18', + type: 'count', + }, + ], + override_index_pattern: 0, + }, + ], +} as Panel; + +describe('convertToLens for Top N', () => { + test('should return null for a non supported aggregation', async () => { + const nonSupportedAggModel = { + ...model, + series: [ + { + ...model.series[0], + metrics: [ + { + type: 'sum_of_squares_bucket', + }, + ] as Series['metrics'], + }, + ], + }; + const triggerOptions = await convertToLens(nonSupportedAggModel); + expect(triggerOptions).toBeNull(); + }); + + test('should return options for a supported aggregation', async () => { + const triggerOptions = await convertToLens(model); + expect(triggerOptions).toStrictEqual({ + configuration: { + fill: '0', + gridLinesVisibility: { x: false, yLeft: false, yRight: false }, + legend: { + isVisible: false, + maxLines: 1, + position: 'right', + shouldTruncate: false, + showSingleSeries: false, + }, + valueLabels: true, + tickLabelsVisibility: { x: true, yLeft: false, yRight: false }, + axisTitlesVisibility: { x: false, yLeft: false, yRight: false }, + }, + type: 'lnsXY', + layers: { + '0': { + axisPosition: 'left', + chartType: 'bar_horizontal', + collapseFn: undefined, + indexPatternId: 'test2', + metrics: [ + { + agg: 'count', + color: '#000000', + fieldName: 'document', + isFullReference: false, + params: {}, + }, + ], + palette: { + name: 'default', + type: 'palette', + }, + splitWithDateHistogram: false, + xFieldName: undefined, + xMode: undefined, + timeInterval: 'auto', + dropPartialBuckets: false, + }, + }, + }); + }); + + test('should return timeShift in the params if it is provided', async () => { + const modelWithFill = { + ...model, + series: [ + { + ...model.series[0], + offset_time: '1h', + }, + ], + }; + const triggerOptions = await convertToLens(modelWithFill); + expect(triggerOptions?.layers[0]?.metrics?.[0]?.params?.shift).toBe('1h'); + }); + + test('should return filter in the params if it is provided', async () => { + const modelWithFill = { + ...model, + series: [ + { + ...model.series[0], + filter: { + language: 'kuery', + query: 'test', + }, + }, + ], + }; + const triggerOptions = await convertToLens(modelWithFill); + expect(triggerOptions?.layers[0]?.metrics?.[0]?.params?.kql).toBe('test'); + }); + + test('should return splitFilters information if the chart is broken down by filters', async () => { + const modelWithSplitFilters = { + ...model, + series: [ + { + ...model.series[0], + split_mode: 'filters', + split_filters: [ + { + color: 'rgba(188,0,85,1)', + filter: { + language: 'kuery', + query: '', + }, + id: '89afac60-7d2b-11ec-917c-c18cd38d60b5', + }, + ], + }, + ], + }; + const triggerOptions = await convertToLens(modelWithSplitFilters); + expect(triggerOptions?.layers[0]?.splitFilters).toStrictEqual([ + { + color: 'rgba(188,0,85,1)', + filter: { + language: 'kuery', + query: '', + }, + id: '89afac60-7d2b-11ec-917c-c18cd38d60b5', + }, + ]); + }); + + test('should return termsParams information if the chart is broken down by terms including series agg collapse fn', async () => { + const modelWithTerms = { + ...model, + series: [ + { + ...model.series[0], + metrics: [ + ...model.series[0].metrics, + { + type: 'series_agg', + function: 'sum', + }, + ], + split_mode: 'terms', + terms_size: 6, + terms_direction: 'desc', + terms_order_by: '_key', + }, + ] as unknown as Series[], + }; + const triggerOptions = await convertToLens(modelWithTerms); + expect(triggerOptions?.layers[0]?.collapseFn).toStrictEqual('sum'); + expect(triggerOptions?.layers[0]?.termsParams).toStrictEqual({ + size: 6, + otherBucket: false, + orderDirection: 'desc', + orderBy: { type: 'alphabetical' }, + includeIsRegex: false, + excludeIsRegex: false, + parentFormat: { + id: 'terms', + }, + }); + }); + + test('should return include exclude information if the chart is broken down by terms', async () => { + const modelWithTerms = { + ...model, + series: [ + { + ...model.series[0], + split_mode: 'terms', + terms_size: 6, + terms_direction: 'desc', + terms_order_by: '_key', + terms_include: 't.*', + }, + ] as unknown as Series[], + }; + const triggerOptions = await convertToLens(modelWithTerms); + expect(triggerOptions?.layers[0]?.termsParams).toStrictEqual({ + size: 6, + otherBucket: false, + orderDirection: 'desc', + orderBy: { type: 'alphabetical' }, + includeIsRegex: true, + include: ['t.*'], + excludeIsRegex: false, + parentFormat: { + id: 'terms', + }, + }); + }); + + test('should return custom time interval if it is given', async () => { + const modelWithTerms = { + ...model, + interval: '1h', + }; + const triggerOptions = await convertToLens(modelWithTerms); + expect(triggerOptions?.layers[0]?.timeInterval).toBe('1h'); + }); + + test('should return dropPartialbuckets if enabled', async () => { + const modelWithDropBuckets = { + ...model, + drop_last_bucket: 1, + }; + const triggerOptions = await convertToLens(modelWithDropBuckets); + expect(triggerOptions?.layers[0]?.dropPartialBuckets).toBe(true); + }); + + test('should return the correct chart configuration', async () => { + const modelWithConfig = { + ...model, + show_legend: 1, + legend_position: 'bottom', + truncate_legend: 0, + show_grid: 1, + series: [{ ...model.series[0], fill: '0.3', separate_axis: 1, axis_position: 'right' }], + }; + const triggerOptions = await convertToLens(modelWithConfig); + expect(triggerOptions).toStrictEqual({ + configuration: { + fill: '0.3', + gridLinesVisibility: { x: false, yLeft: false, yRight: false }, + legend: { + isVisible: true, + maxLines: 1, + position: 'bottom', + shouldTruncate: false, + showSingleSeries: true, + }, + valueLabels: true, + tickLabelsVisibility: { x: true, yLeft: false, yRight: false }, + axisTitlesVisibility: { x: false, yLeft: false, yRight: false }, + }, + type: 'lnsXY', + layers: { + '0': { + axisPosition: 'right', + chartType: 'bar_horizontal', + collapseFn: undefined, + indexPatternId: 'test2', + metrics: [ + { + agg: 'count', + color: '#000000', + fieldName: 'document', + isFullReference: false, + params: {}, + }, + ], + palette: { + name: 'default', + type: 'palette', + }, + splitWithDateHistogram: false, + xFieldName: undefined, + xMode: undefined, + timeInterval: 'auto', + dropPartialBuckets: false, + }, + }, + }); + }); +}); diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/top_n/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/top_n/index.ts new file mode 100644 index 0000000000000..292a44aaf98d9 --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/top_n/index.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 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 { VisualizeEditorLayersContext } from '@kbn/visualizations-plugin/public'; +import { PANEL_TYPES, TIME_RANGE_DATA_MODES } from '../../../common/enums'; +import { getDataViewsStart } from '../../services'; +import { getDataSourceInfo } from '../lib/datasource'; +import { getSeries } from '../lib/series'; +import { getFieldsForTerms } from '../../../common/fields_utils'; +import { ConvertTsvbToLensVisualization } from '../types'; +import { isSplitWithDateHistogram } from '../lib/split_chart'; +import { getLayerConfiguration } from '../lib/layers'; +import { getWindow, isValidMetrics } from '../lib/metrics'; + +export const convertToLens: ConvertTsvbToLensVisualization = async (model, timeRange) => { + const layersConfiguration: { [key: string]: VisualizeEditorLayersContext } = {}; + + // get the active series number + const seriesNum = model.series.filter((series) => !series.hidden).length; + const dataViews = getDataViewsStart(); + + // handle multiple layers/series + for (let layerIdx = 0; layerIdx < model.series.length; layerIdx++) { + const layer = model.series[layerIdx]; + if (layer.hidden) { + continue; + } + + if (!isValidMetrics(layer.metrics, PANEL_TYPES.TOP_N, layer.time_range_mode)) { + return null; + } + + const { indexPatternId } = await getDataSourceInfo( + model.index_pattern, + model.time_field, + Boolean(layer.override_index_pattern), + layer.series_index_pattern, + dataViews + ); + + const window = + model.time_range_mode === TIME_RANGE_DATA_MODES.LAST_VALUE + ? getWindow(model.interval, timeRange) + : undefined; + + // handle multiple metrics + const series = getSeries(layer.metrics, seriesNum, layer.split_mode, layer.color, window); + if (!series || !series.metrics) { + return null; + } + + const splitFields = getFieldsForTerms(layer.terms_field); + + // in case of terms in a date field, we want to apply the date_histogram + const splitWithDateHistogram = await isSplitWithDateHistogram( + layer, + splitFields, + indexPatternId, + dataViews + ); + + if (splitWithDateHistogram === null) { + return null; + } + + layersConfiguration[layerIdx] = getLayerConfiguration( + indexPatternId, + layerIdx, + 'bar_horizontal', + model, + series, + splitFields, + undefined, + undefined, + splitWithDateHistogram, + window + ); + } + + return { + layers: layersConfiguration, + type: 'lnsXY', + configuration: { + fill: model.series[0].fill ?? 0.3, + legend: { + isVisible: Boolean(model.show_legend), + showSingleSeries: Boolean(model.show_legend), + position: model.legend_position ?? 'right', + shouldTruncate: Boolean(model.truncate_legend), + maxLines: model.max_lines_legend ?? 1, + }, + gridLinesVisibility: { + x: false, + yLeft: false, + yRight: false, + }, + tickLabelsVisibility: { + x: true, + yLeft: false, + yRight: false, + }, + axisTitlesVisibility: { + x: false, + yLeft: false, + yRight: false, + }, + valueLabels: true, + }, + }; +}; diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/types.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/types.ts index 8c17b9ec6ce57..cf91d9835bac7 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/types.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/types.ts @@ -6,11 +6,13 @@ * Side Public License, v 1. */ +import { TimeRange } from '@kbn/data-plugin/common'; import { NavigateToLensContext } from '@kbn/visualizations-plugin/public'; import type { Panel } from '../../common/types'; export type ConvertTsvbToLensVisualization = ( - model: Panel + model: Panel, + timeRange?: TimeRange ) => Promise; export interface Filter { diff --git a/src/plugins/vis_types/timeseries/public/metrics_type.ts b/src/plugins/vis_types/timeseries/public/metrics_type.ts index 10658b264c4a3..edd4ac0297b3c 100644 --- a/src/plugins/vis_types/timeseries/public/metrics_type.ts +++ b/src/plugins/vis_types/timeseries/public/metrics_type.ts @@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n'; import uuid from 'uuid/v4'; import type { DataViewsContract, DataView } from '@kbn/data-views-plugin/public'; +import { TimeRange } from '@kbn/data-plugin/common'; import { Vis, VIS_EVENT_TO_TRIGGER, @@ -168,8 +169,8 @@ export const metricsVisDefinition: VisTypeDefinition< } return []; }, - navigateToLens: async (params?: VisParams) => - params ? await convertTSVBtoLensConfiguration(params as Panel) : null, + navigateToLens: async (params?: VisParams, timeRange?: TimeRange) => + params ? await convertTSVBtoLensConfiguration(params as Panel, timeRange) : null, inspectorAdapters: () => ({ requests: new RequestAdapter(), diff --git a/src/plugins/visualizations/public/vis_types/types.ts b/src/plugins/visualizations/public/vis_types/types.ts index 4267e6a0d8aef..ebcdcf594fdfd 100644 --- a/src/plugins/visualizations/public/vis_types/types.ts +++ b/src/plugins/visualizations/public/vis_types/types.ts @@ -10,6 +10,7 @@ import type { IconType } from '@elastic/eui'; import type { ReactNode } from 'react'; import type { PaletteOutput } from '@kbn/coloring'; import type { Adapters } from '@kbn/inspector-plugin/common'; +import { TimeRange } from '@kbn/data-plugin/common'; import type { Query } from '@kbn/es-query'; import type { AggGroupNames, AggParam, AggGroupName } from '@kbn/data-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/public'; @@ -92,7 +93,8 @@ interface VisualizeEditorMetricContext { export interface VisualizeEditorLayersContext { indexPatternId: string; splitWithDateHistogram?: boolean; - timeFieldName?: string; + xFieldName?: string; + xMode?: string; chartType?: string; axisPosition?: string; termsParams?: Record; @@ -134,7 +136,18 @@ export interface NavigateToLensContext { yLeft: boolean; yRight: boolean; }; - extents: { + tickLabelsVisibility?: { + x: boolean; + yLeft: boolean; + yRight: boolean; + }; + axisTitlesVisibility?: { + x: boolean; + yLeft: boolean; + yRight: boolean; + }; + valueLabels?: boolean; + extents?: { yLeftExtent: AxisExtents; yRightExtent: AxisExtents; }; @@ -173,7 +186,8 @@ export interface VisTypeDefinition { * in order to be displayed in the Lens editor. */ readonly navigateToLens?: ( - params?: VisParams + params?: VisParams, + timeRange?: TimeRange ) => Promise | undefined; /** diff --git a/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx b/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx index ca373b31b6020..c80492b45c5cb 100644 --- a/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx +++ b/src/plugins/visualizations/public/visualize_app/components/visualize_top_nav.tsx @@ -99,12 +99,15 @@ const TopNav = ({ useEffect(() => { const asyncGetTriggerContext = async () => { if (vis.type.navigateToLens) { - const triggerConfig = await vis.type.navigateToLens(vis.params); + const triggerConfig = await vis.type.navigateToLens( + vis.params, + services.data.query.timefilter.timefilter.getAbsoluteTime() + ); setEditInLensConfig(triggerConfig); } }; asyncGetTriggerContext(); - }, [vis.params, vis.type]); + }, [services.data.query.timefilter.timefilter, vis.params, vis.type]); const displayEditInLensItem = Boolean(vis.type.navigateToLens && editInLensConfig); const config = useMemo(() => { diff --git a/test/api_integration/apis/index.ts b/test/api_integration/apis/index.ts index f7801f4d42e71..3b8a182308e77 100644 --- a/test/api_integration/apis/index.ts +++ b/test/api_integration/apis/index.ts @@ -28,6 +28,7 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./stats')); loadTestFile(require.resolve('./ui_metric')); loadTestFile(require.resolve('./ui_counters')); + loadTestFile(require.resolve('./unified_field_list')); loadTestFile(require.resolve('./telemetry')); }); } diff --git a/x-pack/test/api_integration/apis/lens/field_stats.ts b/test/api_integration/apis/unified_field_list/field_stats.ts similarity index 92% rename from x-pack/test/api_integration/apis/lens/field_stats.ts rename to test/api_integration/apis/unified_field_list/field_stats.ts index 4d38b54b02252..b489dea50d740 100644 --- a/x-pack/test/api_integration/apis/lens/field_stats.ts +++ b/test/api_integration/apis/unified_field_list/field_stats.ts @@ -1,8 +1,9 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 expect from '@kbn/expect'; @@ -19,8 +20,9 @@ export default ({ getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const supertest = getService('supertest'); + const API_PATH = '/api/unified_field_list/field_stats'; - describe('index stats apis', () => { + describe('field stats apis', () => { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); }); @@ -41,13 +43,13 @@ export default ({ getService }: FtrProviderContext) => { it('should return a 404 for missing index patterns', async () => { await supertest - .post('/api/lens/index_stats/123/field') + .post(API_PATH) .set(COMMON_HEADERS) .send({ + dataViewId: '123', dslQuery: { match_all: {} }, fromDate: TEST_START_TIME, toDate: TEST_END_TIME, - timeFieldName: '@timestamp', fieldName: 'bytes', }) .expect(404); @@ -55,9 +57,10 @@ export default ({ getService }: FtrProviderContext) => { it('should also work without specifying a time field', async () => { const { body } = await supertest - .post('/api/lens/index_stats/logstash-2015.09.22/field') + .post(API_PATH) .set(COMMON_HEADERS) .send({ + dataViewId: 'logstash-2015.09.22', dslQuery: { match_all: {} }, fromDate: TEST_START_TIME, toDate: TEST_END_TIME, @@ -70,9 +73,10 @@ export default ({ getService }: FtrProviderContext) => { it('should return an auto histogram for numbers and top values', async () => { const { body } = await supertest - .post('/api/lens/index_stats/logstash-2015.09.22/field') + .post(API_PATH) .set(COMMON_HEADERS) .send({ + dataViewId: 'logstash-2015.09.22', dslQuery: { match_all: {} }, fromDate: TEST_START_TIME, toDate: TEST_END_TIME, @@ -177,9 +181,10 @@ export default ({ getService }: FtrProviderContext) => { it('should return an auto histogram for dates', async () => { const { body } = await supertest - .post('/api/lens/index_stats/logstash-2015.09.22/field') + .post(API_PATH) .set(COMMON_HEADERS) .send({ + dataViewId: 'logstash-2015.09.22', dslQuery: { match_all: {} }, fromDate: TEST_START_TIME, toDate: TEST_END_TIME, @@ -210,9 +215,10 @@ export default ({ getService }: FtrProviderContext) => { it('should return top values for strings', async () => { const { body } = await supertest - .post('/api/lens/index_stats/logstash-2015.09.22/field') + .post(API_PATH) .set(COMMON_HEADERS) .send({ + dataViewId: 'logstash-2015.09.22', dslQuery: { match_all: {} }, fromDate: TEST_START_TIME, toDate: TEST_END_TIME, @@ -273,9 +279,10 @@ export default ({ getService }: FtrProviderContext) => { it('should return top values for ip fields', async () => { const { body } = await supertest - .post('/api/lens/index_stats/logstash-2015.09.22/field') + .post(API_PATH) .set(COMMON_HEADERS) .send({ + dataViewId: 'logstash-2015.09.22', dslQuery: { match_all: {} }, fromDate: TEST_START_TIME, toDate: TEST_END_TIME, @@ -336,9 +343,10 @@ export default ({ getService }: FtrProviderContext) => { it('should return histograms for scripted date fields', async () => { const { body } = await supertest - .post('/api/lens/index_stats/logstash-2015.09.22/field') + .post(API_PATH) .set(COMMON_HEADERS) .send({ + dataViewId: 'logstash-2015.09.22', dslQuery: { match_all: {} }, fromDate: TEST_START_TIME, toDate: TEST_END_TIME, @@ -361,9 +369,10 @@ export default ({ getService }: FtrProviderContext) => { it('should return top values for scripted string fields', async () => { const { body } = await supertest - .post('/api/lens/index_stats/logstash-2015.09.22/field') + .post(API_PATH) .set(COMMON_HEADERS) .send({ + dataViewId: 'logstash-2015.09.22', dslQuery: { match_all: {} }, fromDate: TEST_START_TIME, toDate: TEST_END_TIME, @@ -388,9 +397,10 @@ export default ({ getService }: FtrProviderContext) => { it('should return top values for index pattern runtime string fields', async () => { const { body } = await supertest - .post('/api/lens/index_stats/logstash-2015.09.22/field') + .post(API_PATH) .set(COMMON_HEADERS) .send({ + dataViewId: 'logstash-2015.09.22', dslQuery: { match_all: {} }, fromDate: TEST_START_TIME, toDate: TEST_END_TIME, @@ -415,9 +425,10 @@ export default ({ getService }: FtrProviderContext) => { it('should apply filters and queries', async () => { const { body } = await supertest - .post('/api/lens/index_stats/logstash-2015.09.22/field') + .post(API_PATH) .set(COMMON_HEADERS) .send({ + dataViewId: 'logstash-2015.09.22', dslQuery: { bool: { filter: [{ match: { 'geo.src': 'US' } }], @@ -434,9 +445,10 @@ export default ({ getService }: FtrProviderContext) => { it('should allow filtering on a runtime field other than the field in use', async () => { const { body } = await supertest - .post('/api/lens/index_stats/logstash-2015.09.22/field') + .post(API_PATH) .set(COMMON_HEADERS) .send({ + dataViewId: 'logstash-2015.09.22', dslQuery: { bool: { filter: [{ exists: { field: 'runtime_string_field' } }], @@ -477,9 +489,10 @@ export default ({ getService }: FtrProviderContext) => { it('should return an auto histogram for precalculated histograms', async () => { const { body } = await supertest - .post('/api/lens/index_stats/histogram-test/field') + .post(API_PATH) .set(COMMON_HEADERS) .send({ + dataViewId: 'histogram-test', dslQuery: { match_all: {} }, fromDate: TEST_START_TIME, toDate: TEST_END_TIME, @@ -545,9 +558,10 @@ export default ({ getService }: FtrProviderContext) => { it('should return a single-value histogram when filtering a precalculated histogram', async () => { const { body } = await supertest - .post('/api/lens/index_stats/histogram-test/field') + .post(API_PATH) .set(COMMON_HEADERS) .send({ + dataViewId: 'histogram-test', dslQuery: { match: { 'histogram-title': 'single value' } }, fromDate: TEST_START_TIME, toDate: TEST_END_TIME, diff --git a/test/api_integration/apis/unified_field_list/index.ts b/test/api_integration/apis/unified_field_list/index.ts new file mode 100644 index 0000000000000..da0a6098e0a42 --- /dev/null +++ b/test/api_integration/apis/unified_field_list/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export default function lensApiIntegrationTests({ loadTestFile }: FtrProviderContext) { + describe('UnifiedFieldList', () => { + loadTestFile(require.resolve('./field_stats')); + }); +} diff --git a/test/functional/apps/console/_comments.ts b/test/functional/apps/console/_comments.ts index fe0fd882c607f..9132e0f04b0c8 100644 --- a/test/functional/apps/console/_comments.ts +++ b/test/functional/apps/console/_comments.ts @@ -15,7 +15,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const PageObjects = getPageObjects(['common', 'console', 'header']); - describe('console app', function testComments() { + // Failing: See https://github.com/elastic/kibana/issues/139295 + describe.skip('console app', function testComments() { this.tags('includeFirefox'); before(async () => { log.debug('navigateTo console'); @@ -46,7 +47,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); } - describe('with single line comments', async () => { + // FLAKY: https://github.com/elastic/kibana/issues/138160 + describe.skip('with single line comments', async () => { await runTests( [ { diff --git a/test/functional/apps/dashboard/group1/url_field_formatter.ts b/test/functional/apps/dashboard/group1/url_field_formatter.ts index ec6b39d7dc659..9cdebca739635 100644 --- a/test/functional/apps/dashboard/group1/url_field_formatter.ts +++ b/test/functional/apps/dashboard/group1/url_field_formatter.ts @@ -24,6 +24,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const fieldName = 'clientip'; const deployment = getService('deployment'); const retry = getService('retry'); + const security = getService('security'); const clickFieldAndCheckUrl = async (fieldLink: WebElementWrapper) => { const fieldValue = await fieldLink.getVisibleText(); @@ -38,6 +39,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('Changing field formatter to Url', () => { before(async function () { + await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader', 'animals']); await kibanaServer.savedObjects.cleanStandardList(); await kibanaServer.importExport.load( 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' @@ -57,6 +59,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { after(async () => { await kibanaServer.savedObjects.cleanStandardList(); + await security.testUser.restoreDefaults(); }); it('applied on dashboard', async () => { diff --git a/test/functional/apps/dashboard/group6/dashboard_query_bar.ts b/test/functional/apps/dashboard/group6/dashboard_query_bar.ts index 6890ba5bed24f..010aec9607816 100644 --- a/test/functional/apps/dashboard/group6/dashboard_query_bar.ts +++ b/test/functional/apps/dashboard/group6/dashboard_query_bar.ts @@ -16,10 +16,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const pieChart = getService('pieChart'); const queryBar = getService('queryBar'); const retry = getService('retry'); + const security = getService('security'); const PageObjects = getPageObjects(['common', 'dashboard', 'discover']); describe('dashboard query bar', () => { before(async () => { + await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader', 'animals']); await kibanaServer.savedObjects.cleanStandardList(); await kibanaServer.importExport.load( 'test/functional/fixtures/kbn_archiver/dashboard/current/kibana' @@ -34,6 +36,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { after(async () => { await kibanaServer.savedObjects.cleanStandardList(); + await security.testUser.restoreDefaults(); }); it('causes panels to reload when refresh is clicked', async () => { diff --git a/test/functional/apps/dashboard_elements/controls/control_group_chaining.ts b/test/functional/apps/dashboard_elements/controls/control_group_chaining.ts index ba25fcfce98e2..a27a1a4814cfb 100644 --- a/test/functional/apps/dashboard_elements/controls/control_group_chaining.ts +++ b/test/functional/apps/dashboard_elements/controls/control_group_chaining.ts @@ -13,6 +13,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); + const security = getService('security'); const { dashboardControls, common, dashboard, timePicker } = getPageObjects([ 'dashboardControls', 'timePicker', @@ -32,6 +33,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }; before(async () => { + await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader', 'animals']); await common.navigateToApp('dashboard'); await dashboard.gotoDashboardLandingPage(); await dashboard.clickNewDashboard(); @@ -62,6 +64,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { controlIds = await dashboardControls.getAllControlIds(); }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + it('Shows all available options in first Options List control', async () => { await dashboardControls.optionsListOpenPopover(controlIds[0]); await retry.try(async () => { diff --git a/test/functional/apps/dashboard_elements/controls/options_list.ts b/test/functional/apps/dashboard_elements/controls/options_list.ts index c6847d2ab618c..d6f49b9b435e8 100644 --- a/test/functional/apps/dashboard_elements/controls/options_list.ts +++ b/test/functional/apps/dashboard_elements/controls/options_list.ts @@ -15,6 +15,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const queryBar = getService('queryBar'); const pieChart = getService('pieChart'); + const security = getService('security'); const elasticChart = getService('elasticChart'); const filterBar = getService('filterBar'); const testSubjects = getService('testSubjects'); @@ -32,6 +33,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('Dashboard options list integration', () => { before(async () => { + await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader', 'animals']); await common.navigateToApp('dashboard'); await dashboard.gotoDashboardLandingPage(); await dashboard.clickNewDashboard(); @@ -422,6 +424,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await filterBar.removeAllFilters(); await queryBar.clickQuerySubmitButton(); await dashboardControls.clearAllControls(); + await security.testUser.restoreDefaults(); }); }); }); diff --git a/test/functional/apps/dashboard_elements/controls/replace_controls.ts b/test/functional/apps/dashboard_elements/controls/replace_controls.ts index e81788a1946a9..2d1f4509f04ce 100644 --- a/test/functional/apps/dashboard_elements/controls/replace_controls.ts +++ b/test/functional/apps/dashboard_elements/controls/replace_controls.ts @@ -17,6 +17,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const retry = getService('retry'); + const security = getService('security'); const { dashboardControls, timePicker, common, dashboard } = getPageObjects([ 'dashboardControls', @@ -59,12 +60,17 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { before(async () => { await common.navigateToApp('dashboard'); + await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader', 'animals']); await dashboard.gotoDashboardLandingPage(); await dashboard.clickNewDashboard(); await timePicker.setDefaultDataRange(); await dashboard.saveDashboard(DASHBOARD_NAME, { exitFromEditMode: false }); }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + describe('Replace options list', async () => { beforeEach(async () => { await dashboardControls.clearAllControls(); diff --git a/test/functional/apps/discover/group2/_data_grid_context.ts b/test/functional/apps/discover/group2/_data_grid_context.ts index 549e02f60137f..4903cd446dfc9 100644 --- a/test/functional/apps/discover/group2/_data_grid_context.ts +++ b/test/functional/apps/discover/group2/_data_grid_context.ts @@ -16,6 +16,7 @@ const TEST_FILTER_COLUMN_NAMES = [ ]; export default function ({ getService, getPageObjects }: FtrProviderContext) { + const log = getService('log'); const retry = getService('retry'); const filterBar = getService('filterBar'); const dataGrid = getService('dataGrid'); @@ -114,6 +115,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.header.waitUntilLoadingHasFinished(); await retry.waitFor('document table has a length of 6', async () => { const nrOfDocs = (await dataGrid.getBodyRows()).length; + log.debug('document table length', nrOfDocs); return nrOfDocs === 6; }); }); diff --git a/test/functional/apps/management/_data_view_relationships.ts b/test/functional/apps/management/_data_view_relationships.ts index c8e78eab4ce64..680500f58a60c 100644 --- a/test/functional/apps/management/_data_view_relationships.ts +++ b/test/functional/apps/management/_data_view_relationships.ts @@ -29,6 +29,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.settings.clickKibanaIndexPatterns(); await PageObjects.settings.clickIndexPatternLogstash(); await PageObjects.settings.clickRelationshipsTab(); + await PageObjects.header.waitUntilLoadingHasFinished(); expect(parseInt(await PageObjects.settings.getRelationshipsTabCount(), 10)).to.be(1); }); }); diff --git a/test/functional/apps/visualize/group5/_tsvb_time_series.ts b/test/functional/apps/visualize/group5/_tsvb_time_series.ts index 409b2b3610f5c..f9e016ff1f635 100644 --- a/test/functional/apps/visualize/group5/_tsvb_time_series.ts +++ b/test/functional/apps/visualize/group5/_tsvb_time_series.ts @@ -193,6 +193,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); await testSubjects.click('applyFiltersPopoverButton'); + await dashboard.waitForRenderComplete(); }; const cleanup = async () => { diff --git a/test/functional/fixtures/es_archiver/deprecations_service/mappings.json b/test/functional/fixtures/es_archiver/deprecations_service/mappings.json index 2d86b863d47dc..47bd39ebf8bcf 100644 --- a/test/functional/fixtures/es_archiver/deprecations_service/mappings.json +++ b/test/functional/fixtures/es_archiver/deprecations_service/mappings.json @@ -423,7 +423,10 @@ "number_of_shards": "1", "priority": "10", "refresh_interval": "1s", - "routing_partition_size": "1" + "routing_partition_size": "1", + "mapping": { + "total_fields": { "limit": 1500 } + } } } } diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/export_transform/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/export_transform/mappings.json index 9562b381a40f8..84628ef0366fa 100644 --- a/test/functional/fixtures/es_archiver/saved_objects_management/export_transform/mappings.json +++ b/test/functional/fixtures/es_archiver/saved_objects_management/export_transform/mappings.json @@ -462,7 +462,10 @@ "number_of_shards": "1", "priority": "10", "refresh_interval": "1s", - "routing_partition_size": "1" + "routing_partition_size": "1", + "mapping": { + "total_fields": { "limit": 1500 } + } } } } diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/hidden_saved_objects/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/hidden_saved_objects/mappings.json index 780fdda5f7cbe..43711680a1f12 100644 --- a/test/functional/fixtures/es_archiver/saved_objects_management/hidden_saved_objects/mappings.json +++ b/test/functional/fixtures/es_archiver/saved_objects_management/hidden_saved_objects/mappings.json @@ -470,7 +470,10 @@ "number_of_shards": "1", "priority": "10", "refresh_interval": "1s", - "routing_partition_size": "1" + "routing_partition_size": "1", + "mapping": { + "total_fields": { "limit": 1500 } + } } } } diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/nested_export_transform/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/nested_export_transform/mappings.json index 9562b381a40f8..84628ef0366fa 100644 --- a/test/functional/fixtures/es_archiver/saved_objects_management/nested_export_transform/mappings.json +++ b/test/functional/fixtures/es_archiver/saved_objects_management/nested_export_transform/mappings.json @@ -462,7 +462,10 @@ "number_of_shards": "1", "priority": "10", "refresh_interval": "1s", - "routing_partition_size": "1" + "routing_partition_size": "1", + "mapping": { + "total_fields": { "limit": 1500 } + } } } } diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management/mappings.json index 3c0a975a30be0..6ffcd71f29e50 100644 --- a/test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management/mappings.json +++ b/test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management/mappings.json @@ -470,7 +470,10 @@ "number_of_shards": "1", "priority": "10", "refresh_interval": "1s", - "routing_partition_size": "1" + "routing_partition_size": "1", + "mapping": { + "total_fields": { "limit": 1500 } + } } } } diff --git a/test/functional/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts index b3c0f7deda982..91b852849c0ce 100644 --- a/test/functional/page_objects/common_page.ts +++ b/test/functional/page_objects/common_page.ts @@ -177,8 +177,8 @@ export class CommonPageObject extends FtrService { ) { const appConfig = { // subUrl following the basePath, assumes no hashes. Ex: 'app/endpoint/management' - pathname: `${basePath}${this.config.get(['apps', appName]).pathname}${subUrl}`, - search, + pathname: `${basePath}${this.config.get(['apps', appName]).pathname}${subUrl || ''}`, + search: search || '', }; await this.navigate({ diff --git a/test/functional/services/filter_bar.ts b/test/functional/services/filter_bar.ts index ab020a1ae943d..16ce34d446d1c 100644 --- a/test/functional/services/filter_bar.ts +++ b/test/functional/services/filter_bar.ts @@ -56,8 +56,10 @@ export class FilterBarService extends FtrService { * @param key field name */ public async removeFilter(key: string): Promise { - await this.testSubjects.click(`~filter & ~filter-key-${key}`); - await this.testSubjects.click(`deleteFilter`); + await this.retry.try(async () => { + await this.testSubjects.click(`~filter & ~filter-key-${key}`); + await this.testSubjects.click(`deleteFilter`); + }); await this.header.awaitGlobalLoadingIndicatorHidden(); } diff --git a/test/harden/child_process.js b/test/harden/child_process.js index cd1c54aa5db2d..f15f5aceb39e7 100644 --- a/test/harden/child_process.js +++ b/test/harden/child_process.js @@ -25,10 +25,7 @@ test('test setup ok', (t) => { t.end(); }); -// TODO: fork() has been omitted as it doesn't validate its arguments in -// Node.js 10 and will throw an internal error asynchronously. This is fixed in -// newer versions. See https://github.com/elastic/kibana/issues/59628 -const functions = ['exec', 'execFile', 'spawn', 'execFileSync', 'execSync', 'spawnSync']; +const functions = ['exec', 'execFile', 'fork', 'spawn', 'execFileSync', 'execSync', 'spawnSync']; for (const name of functions) { test(`${name}()`, (t) => { t.throws(() => cp[name](), /argument must be of type string/); diff --git a/tsconfig.base.json b/tsconfig.base.json index 68882faec661a..22aeada1359fa 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -161,6 +161,8 @@ "@kbn/ui-actions-enhanced-plugin/*": ["src/plugins/ui_actions_enhanced/*"], "@kbn/ui-actions-plugin": ["src/plugins/ui_actions"], "@kbn/ui-actions-plugin/*": ["src/plugins/ui_actions/*"], + "@kbn/unified-field-list-plugin": ["src/plugins/unified_field_list"], + "@kbn/unified-field-list-plugin/*": ["src/plugins/unified_field_list/*"], "@kbn/unified-search-plugin": ["src/plugins/unified_search"], "@kbn/unified-search-plugin/*": ["src/plugins/unified_search/*"], "@kbn/url-forwarding-plugin": ["src/plugins/url_forwarding"], @@ -273,6 +275,8 @@ "@kbn/embedded-lens-example-plugin/*": ["x-pack/examples/embedded_lens_example/*"], "@kbn/exploratory-view-example-plugin": ["x-pack/examples/exploratory_view_example"], "@kbn/exploratory-view-example-plugin/*": ["x-pack/examples/exploratory_view_example/*"], + "@kbn/files-example-plugin": ["x-pack/examples/files_example"], + "@kbn/files-example-plugin/*": ["x-pack/examples/files_example/*"], "@kbn/reporting-example-plugin": ["x-pack/examples/reporting_example"], "@kbn/reporting-example-plugin/*": ["x-pack/examples/reporting_example/*"], "@kbn/screenshotting-example-plugin": ["x-pack/examples/screenshotting_example"], diff --git a/x-pack/examples/files_example/.i18nrc.json b/x-pack/examples/files_example/.i18nrc.json new file mode 100644 index 0000000000000..79fdf5a24917c --- /dev/null +++ b/x-pack/examples/files_example/.i18nrc.json @@ -0,0 +1,7 @@ +{ + "prefix": "filesExample", + "paths": { + "filesExample": "." + }, + "translations": ["translations/ja-JP.json"] +} diff --git a/x-pack/examples/files_example/README.md b/x-pack/examples/files_example/README.md new file mode 100644 index 0000000000000..6db1391df5873 --- /dev/null +++ b/x-pack/examples/files_example/README.md @@ -0,0 +1,4 @@ +# filesExample + +An example plugin that integrates with the Kibana "files" plugin. + diff --git a/x-pack/examples/files_example/common/index.ts b/x-pack/examples/files_example/common/index.ts new file mode 100644 index 0000000000000..a58065ad7fd98 --- /dev/null +++ b/x-pack/examples/files_example/common/index.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FileKind } from '@kbn/files-plugin/common'; + +export const PLUGIN_ID = 'filesExample'; +export const PLUGIN_NAME = 'filesExample'; + +const httpTags = { + tags: [`access:${PLUGIN_ID}`], +}; + +export const exampleFileKind: FileKind = { + id: 'filesExample', + http: { + create: httpTags, + delete: httpTags, + download: httpTags, + getById: httpTags, + list: httpTags, + share: httpTags, + update: httpTags, + }, +}; diff --git a/x-pack/examples/files_example/kibana.json b/x-pack/examples/files_example/kibana.json new file mode 100644 index 0000000000000..5df1141929c41 --- /dev/null +++ b/x-pack/examples/files_example/kibana.json @@ -0,0 +1,14 @@ +{ + "id": "filesExample", + "version": "1.0.0", + "kibanaVersion": "kibana", + "owner": { + "name": "kibana-app-services", + "githubTeam": "kibana-app-services" + }, + "description": "Example plugin integrating with files plugin", + "server": true, + "ui": true, + "requiredPlugins": ["files"], + "optionalPlugins": [] +} diff --git a/x-pack/examples/files_example/public/application.tsx b/x-pack/examples/files_example/public/application.tsx new file mode 100644 index 0000000000000..7f599ded82b6f --- /dev/null +++ b/x-pack/examples/files_example/public/application.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 React from 'react'; +import ReactDOM from 'react-dom'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { AppMountParameters, CoreStart } from '@kbn/core/public'; +import { AppPluginStartDependencies } from './types'; +import { FilesExampleApp } from './components/app'; + +const queryClient = new QueryClient(); + +export const renderApp = ( + { notifications }: CoreStart, + { files }: AppPluginStartDependencies, + { element }: AppMountParameters +) => { + ReactDOM.render( + + + , + element + ); + + return () => ReactDOM.unmountComponentAtNode(element); +}; diff --git a/x-pack/examples/files_example/public/assets/image.png.base64 b/x-pack/examples/files_example/public/assets/image.png.base64 new file mode 100644 index 0000000000000..e85f2ccf1626e --- /dev/null +++ b/x-pack/examples/files_example/public/assets/image.png.base64 @@ -0,0 +1 @@ +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 diff --git a/x-pack/examples/files_example/public/components/app.tsx b/x-pack/examples/files_example/public/components/app.tsx new file mode 100644 index 0000000000000..f25494bf3e3ba --- /dev/null +++ b/x-pack/examples/files_example/public/components/app.tsx @@ -0,0 +1,169 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState } from 'react'; +import { useQuery } from '@tanstack/react-query'; +import type { FileJSON } from '@kbn/files-plugin/common'; +import type { FilesClientResponses } from '@kbn/files-plugin/public'; + +const names = ['foo', 'bar', 'baz']; + +import { + EuiPageTemplate, + EuiInMemoryTable, + EuiInMemoryTableProps, + EuiButton, + EuiIcon, + EuiButtonIcon, + EuiLink, +} from '@elastic/eui'; + +import { CoreStart } from '@kbn/core/public'; +import { DetailsFlyout } from './details_flyout'; +import type { FileClients } from '../types'; +import { ConfirmButtonIcon } from './confirm_button'; +// @ts-ignore +import imageBase64 from '!!raw-loader!../assets/image.png.base64'; + +interface FilesExampleAppDeps { + files: FileClients; + notifications: CoreStart['notifications']; +} + +type ListResponse = FilesClientResponses['list']; + +export const FilesExampleApp = ({ files, notifications }: FilesExampleAppDeps) => { + const { data, isLoading, error, refetch } = useQuery(['files'], () => + files.example.list() + ); + const [isUploadingImage, setIsUploadingImage] = useState(false); + const [isDeletingFile, setIsDeletingFile] = useState(false); + const [selectedItem, setSelectedItem] = useState(); + + const uploadImage = async () => { + try { + setIsUploadingImage(true); + const { file } = await files.example.create({ + name: names[Math.floor(Math.random() * names.length)], + alt: 'My image', + meta: { myValue: 'test' }, + mimeType: 'image/png', + }); + await refetch(); + const blob = new Blob([Uint8Array.from(atob(imageBase64), (c) => c.charCodeAt(0))], { + type: 'image/png', + }); + await files.example.upload({ id: file.id, body: blob }); + await refetch(); + notifications.toasts.addSuccess('Sucessfully uploaded image'); + } finally { + setIsUploadingImage(false); + } + }; + + const renderToolsRight = () => { + return [ + + Upload image + , + ]; + }; + + const items = [...(data?.files ?? [])].reverse(); + + const columns: EuiInMemoryTableProps['columns'] = [ + { + field: 'name', + name: 'Name', + render: (name, item) => setSelectedItem(item)}>{name}, + }, + { + field: 'status', + name: 'Status', + render: (status: FileJSON['status']) => + status === 'READY' ? ( + + ) : status === 'AWAITING_UPLOAD' ? ( + + ) : ( + + ), + }, + { + name: 'Actions', + actions: [ + { + name: 'View', + description: 'View file', + isPrimary: true, + render: (item) => ( + setSelectedItem(item)} + /> + ), + }, + { + name: 'Delete', + description: 'Delete this file', + render: (item) => ( + { + try { + setIsDeletingFile(true); + await files.example.delete({ id: item.id }); + await refetch(); + } finally { + setIsDeletingFile(false); + } + }} + /> + ), + }, + ], + }, + ]; + + return ( + <> + + + + {selectedItem && ( + setSelectedItem(undefined)} + /> + )} + + ); +}; diff --git a/x-pack/examples/files_example/public/components/confirm_button.tsx b/x-pack/examples/files_example/public/components/confirm_button.tsx new file mode 100644 index 0000000000000..05e64243bb374 --- /dev/null +++ b/x-pack/examples/files_example/public/components/confirm_button.tsx @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState, FunctionComponent } from 'react'; +import { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; + +interface Props { + label: string; + confirmationText: string; + onConfirm: () => void; + disabled: boolean; +} + +export const ConfirmButtonIcon: FunctionComponent = ({ + label, + confirmationText, + onConfirm, + disabled, +}) => { + const [showConfirm, setShowConfirm] = useState(false); + + return showConfirm ? ( + + + + ) : ( + void }) => { + e.stopPropagation(); + setShowConfirm(true); + setTimeout(() => setShowConfirm(false), 3000); + }} + /> + ); +}; diff --git a/x-pack/examples/files_example/public/components/details_flyout.tsx b/x-pack/examples/files_example/public/components/details_flyout.tsx new file mode 100644 index 0000000000000..64db3fc851006 --- /dev/null +++ b/x-pack/examples/files_example/public/components/details_flyout.tsx @@ -0,0 +1,100 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import moment from 'moment'; +import type { FunctionComponent } from 'react'; +import React from 'react'; +import { css } from '@emotion/react'; +import { + EuiFlyout, + EuiFlyoutHeader, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, + EuiButton, + EuiTitle, + EuiDescriptionList, + EuiSpacer, +} from '@elastic/eui'; +import type { FileJSON } from '@kbn/files-plugin/common'; +import { FileClients } from '../types'; + +interface Props { + file: FileJSON; + files: FileClients; + onDismiss: () => void; +} + +export const DetailsFlyout: FunctionComponent = ({ files, file, onDismiss }) => { + return ( + + + +

{file.name}

+
+
+ + + + {file.alt + + + + + + Download + + + + + Close + + + + +
+ ); +}; diff --git a/x-pack/examples/files_example/public/index.ts b/x-pack/examples/files_example/public/index.ts new file mode 100644 index 0000000000000..15e472c4f4cc6 --- /dev/null +++ b/x-pack/examples/files_example/public/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FilesExamplePlugin } from './plugin'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. +export function plugin() { + return new FilesExamplePlugin(); +} diff --git a/x-pack/examples/files_example/public/plugin.ts b/x-pack/examples/files_example/public/plugin.ts new file mode 100644 index 0000000000000..ad41c3ce2d44a --- /dev/null +++ b/x-pack/examples/files_example/public/plugin.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { AppMountParameters, CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; +import { PLUGIN_ID, PLUGIN_NAME, exampleFileKind } from '../common'; +import { FilesExamplePluginsStart, FilesExamplePluginsSetup } from './types'; + +export class FilesExamplePlugin + implements Plugin +{ + public setup(core: CoreSetup) { + // Register an application into the side navigation menu + core.application.register({ + id: PLUGIN_ID, + title: PLUGIN_NAME, + async mount(params: AppMountParameters) { + // Load application bundle + const { renderApp } = await import('./application'); + // Get start services as specified in kibana.json + const [coreStart, { files }] = await core.getStartServices(); + // Render the application + return renderApp( + coreStart, + { + files: { + example: files.filesClientFactory.asScoped(exampleFileKind.id), + }, + }, + params + ); + }, + }); + + // Return methods that should be available to other plugins + return {}; + } + + public start(core: CoreStart) { + return {}; + } + + public stop() {} +} diff --git a/x-pack/examples/files_example/public/types.ts b/x-pack/examples/files_example/public/types.ts new file mode 100644 index 0000000000000..2d450fbdad111 --- /dev/null +++ b/x-pack/examples/files_example/public/types.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FilesSetup, FilesStart, FilesClient } from '@kbn/files-plugin/public'; + +export interface FilesExamplePluginsSetup { + files: FilesSetup; +} + +export interface FilesExamplePluginsStart { + files: FilesStart; +} + +export interface FileClients { + // Example file kind + example: FilesClient; +} + +export interface AppPluginStartDependencies { + files: FileClients; +} diff --git a/x-pack/examples/files_example/server/index.ts b/x-pack/examples/files_example/server/index.ts new file mode 100644 index 0000000000000..5b6de87308e5e --- /dev/null +++ b/x-pack/examples/files_example/server/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { PluginInitializerContext } from '@kbn/core/server'; +import { FilesExamplePlugin } from './plugin'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. + +export function plugin(initializerContext: PluginInitializerContext) { + return new FilesExamplePlugin(initializerContext); +} diff --git a/x-pack/examples/files_example/server/plugin.ts b/x-pack/examples/files_example/server/plugin.ts new file mode 100644 index 0000000000000..2b077d6f3e88a --- /dev/null +++ b/x-pack/examples/files_example/server/plugin.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { PluginInitializerContext, CoreSetup, CoreStart, Plugin, Logger } from '@kbn/core/server'; +import { exampleFileKind } from '../common'; +import type { FilesExamplePluginsSetup, FilesExamplePluginsStart } from './types'; + +export class FilesExamplePlugin + implements Plugin +{ + private readonly logger: Logger; + + constructor(initializerContext: PluginInitializerContext) { + this.logger = initializerContext.logger.get(); + } + + public setup(core: CoreSetup, { files }: FilesExamplePluginsSetup) { + this.logger.debug('filesExample: Setup'); + + files.registerFileKind(exampleFileKind); + + return {}; + } + + public start(core: CoreStart) { + this.logger.debug('filesExample: Started'); + return {}; + } + + public stop() {} +} diff --git a/x-pack/examples/files_example/server/types.ts b/x-pack/examples/files_example/server/types.ts new file mode 100644 index 0000000000000..b1cef58620ca5 --- /dev/null +++ b/x-pack/examples/files_example/server/types.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FilesSetup, FilesStart } from '@kbn/files-plugin/server'; + +export interface FilesExamplePluginsSetup { + files: FilesSetup; +} + +export interface FilesExamplePluginsStart { + files: FilesStart; +} diff --git a/x-pack/examples/files_example/tsconfig.json b/x-pack/examples/files_example/tsconfig.json new file mode 100644 index 0000000000000..caeb25650a142 --- /dev/null +++ b/x-pack/examples/files_example/tsconfig.json @@ -0,0 +1,25 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target/types" + }, + "include": [ + "index.ts", + "common/**/*", + "public/**/*", + "server/**/*", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + "../../../typings/**/*" + ], + "exclude": [], + "references": [ + { + "path": "../../../src/core/tsconfig.json" + }, + { + "path": "../../plugins/files/tsconfig.json" + } + ] +} diff --git a/x-pack/plugins/actions/server/action_type_registry.test.ts b/x-pack/plugins/actions/server/action_type_registry.test.ts index f8425063de047..bad09ce70207d 100644 --- a/x-pack/plugins/actions/server/action_type_registry.test.ts +++ b/x-pack/plugins/actions/server/action_type_registry.test.ts @@ -169,6 +169,24 @@ describe('register()', () => { expect(getRetry(0, new ExecutorError('my message', {}, retryTime))).toEqual(retryTime); }); + test('provides a getRetry function that handles errors based on maxAttempts', () => { + const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); + actionTypeRegistry.register({ + id: 'my-action-type', + name: 'My action type', + minimumLicenseRequired: 'basic', + supportedFeatureIds: ['alerting'], + executor, + maxAttempts: 2, + }); + expect(mockTaskManager.registerTaskDefinitions).toHaveBeenCalledTimes(1); + const registerTaskDefinitionsCall = mockTaskManager.registerTaskDefinitions.mock.calls[0][0]; + const getRetry = registerTaskDefinitionsCall['actions:my-action-type'].getRetry!; + + expect(getRetry(1, new Error())).toEqual(true); + expect(getRetry(2, new Error())).toEqual(false); + }); + test('registers gold+ action types to the licensing feature usage API', () => { const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); actionTypeRegistry.register({ diff --git a/x-pack/plugins/actions/server/action_type_registry.ts b/x-pack/plugins/actions/server/action_type_registry.ts index ed5f731452c45..c105034c7f687 100644 --- a/x-pack/plugins/actions/server/action_type_registry.ts +++ b/x-pack/plugins/actions/server/action_type_registry.ts @@ -156,8 +156,8 @@ export class ActionTypeRegistry { if (error instanceof ExecutorError) { return error.retry == null ? false : error.retry; } - // Don't retry other kinds of errors - return false; + // Only retry other kinds of errors based on attempts + return attempts < (actionType.maxAttempts ?? 0); }, createTaskRunner: (context: RunContext) => this.taskRunnerFactory.create(context, actionType.maxAttempts), diff --git a/x-pack/plugins/actions/server/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client.test.ts index 4576a301a5f2f..44276a604896f 100644 --- a/x-pack/plugins/actions/server/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client.test.ts @@ -45,11 +45,15 @@ import { getOAuthJwtAccessToken } from './builtin_action_types/lib/get_oauth_jwt import { getOAuthClientCredentialsAccessToken } from './builtin_action_types/lib/get_oauth_client_credentials_access_token'; import { OAuthParams } from './routes/get_oauth_access_token'; -jest.mock('@kbn/core/server/saved_objects/service/lib/utils', () => ({ - SavedObjectsUtils: { - generateId: () => 'mock-saved-object-id', - }, -})); +jest.mock('@kbn/core-saved-objects-utils-server', () => { + const actual = jest.requireActual('@kbn/core-saved-objects-utils-server'); + return { + ...actual, + SavedObjectsUtils: { + generateId: () => 'mock-saved-object-id', + }, + }; +}); jest.mock('./lib/track_legacy_rbac_exemption', () => ({ trackLegacyRBACExemption: jest.fn(), diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/connector_token_client.test.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/connector_token_client.test.ts index dfa307ca3cd91..1aa8bbb3da9a7 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/lib/connector_token_client.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/lib/connector_token_client.test.ts @@ -13,11 +13,15 @@ import { Logger } from '@kbn/core/server'; import { ConnectorToken } from '../../types'; const logger = loggingSystemMock.create().get() as jest.Mocked; -jest.mock('@kbn/core/server/saved_objects/service/lib/utils', () => ({ - SavedObjectsUtils: { - generateId: () => 'mock-saved-object-id', - }, -})); +jest.mock('@kbn/core-saved-objects-utils-server', () => { + const actual = jest.requireActual('@kbn/core-saved-objects-utils-server'); + return { + ...actual, + SavedObjectsUtils: { + generateId: () => 'mock-saved-object-id', + }, + }; +}); const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); const encryptedSavedObjectsClient = encryptedSavedObjectsMock.createClient(); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts index 53f9c59545526..b29d41f183b73 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts @@ -26,11 +26,15 @@ jest.mock('../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation bulkMarkApiKeysForInvalidation: jest.fn(), })); -jest.mock('@kbn/core/server/saved_objects/service/lib/utils', () => ({ - SavedObjectsUtils: { - generateId: () => 'mock-saved-object-id', - }, -})); +jest.mock('@kbn/core-saved-objects-utils-server', () => { + const actual = jest.requireActual('@kbn/core-saved-objects-utils-server'); + return { + ...actual, + SavedObjectsUtils: { + generateId: () => 'mock-saved-object-id', + }, + }; +}); const taskManager = taskManagerMock.createStart(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts index 219ee35756f07..9071099ed3aaf 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts @@ -23,11 +23,15 @@ import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; import { bulkMarkApiKeysForInvalidation } from '../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation'; -jest.mock('@kbn/core/server/saved_objects/service/lib/utils', () => ({ - SavedObjectsUtils: { - generateId: () => 'mock-saved-object-id', - }, -})); +jest.mock('@kbn/core-saved-objects-utils-server', () => { + const actual = jest.requireActual('@kbn/core-saved-objects-utils-server'); + return { + ...actual, + SavedObjectsUtils: { + generateId: () => 'mock-saved-object-id', + }, + }; +}); jest.mock('../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation', () => ({ bulkMarkApiKeysForInvalidation: jest.fn(), diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations.ts b/x-pack/plugins/alerting/server/saved_objects/migrations.ts index ddae200ae8fa6..0873ba3e5c936 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations.ts @@ -7,7 +7,7 @@ import { isRuleType, ruleTypeMappings } from '@kbn/securitysolution-rules'; import { isString } from 'lodash/fp'; -import { omit } from 'lodash'; +import { omit, pick } from 'lodash'; import moment from 'moment-timezone'; import { gte } from 'semver'; import { @@ -40,7 +40,8 @@ interface AlertLogMeta extends LogMeta { } type AlertMigration = ( - doc: SavedObjectUnsanitizedDoc + doc: SavedObjectUnsanitizedDoc, + context: SavedObjectMigrationContext ) => SavedObjectUnsanitizedDoc; function createEsoMigration( @@ -169,6 +170,12 @@ export function getMigrations( pipeMigrations(addSearchType, removeInternalTags, convertSnoozes) ); + const migrationRules850 = createEsoMigration( + encryptedSavedObjects, + (doc): doc is SavedObjectUnsanitizedDoc => isEsQueryRuleType(doc), + pipeMigrations(stripOutRuntimeFieldsInOldESQuery) + ); + return mergeSavedObjectMigrationMaps( { '7.10.0': executeMigrationWithErrorHandling(migrationWhenRBACWasIntroduced, '7.10.0'), @@ -182,6 +189,7 @@ export function getMigrations( '8.0.1': executeMigrationWithErrorHandling(migrationRules801, '8.0.1'), '8.2.0': executeMigrationWithErrorHandling(migrationRules820, '8.2.0'), '8.3.0': executeMigrationWithErrorHandling(migrationRules830, '8.3.0'), + '8.5.0': executeMigrationWithErrorHandling(migrationRules850, '8.5.0'), }, getSearchSourceMigrations(encryptedSavedObjects, searchSourceMigrations) ); @@ -750,6 +758,47 @@ function addSecuritySolutionAADRuleTypeTags( : doc; } +function stripOutRuntimeFieldsInOldESQuery( + doc: SavedObjectUnsanitizedDoc, + context: SavedObjectMigrationContext +): SavedObjectUnsanitizedDoc { + const isESDSLrule = + isEsQueryRuleType(doc) && !isSerializedSearchSource(doc.attributes.params.searchConfiguration); + + if (isESDSLrule) { + try { + const parsedQuery = JSON.parse(doc.attributes.params.esQuery as string); + // parsing and restringifying will cause us to lose the formatting so we only do so if this rule has + // fields other than `query` which is the only valid field at this stage + const hasFieldsOtherThanQuery = Object.keys(parsedQuery).some((key) => key !== 'query'); + return hasFieldsOtherThanQuery + ? { + ...doc, + attributes: { + ...doc.attributes, + params: { + ...doc.attributes.params, + esQuery: JSON.stringify(pick(parsedQuery, 'query'), null, 4), + }, + }, + } + : doc; + } catch (err) { + // Instead of failing the upgrade when an unparsable rule is encountered, we log that the rule caouldn't be migrated and + // as a result legacy parameters might cause the rule to behave differently if it is, in fact, still running at all + context.log.error( + `unable to migrate and remove legacy runtime fields in rule ${doc.id} due to invalid query: "${doc.attributes.params.esQuery}" - query must be JSON`, + { + migrations: { + alertDocument: doc, + }, + } + ); + } + } + return doc; +} + function addThreatIndicatorPathToThreatMatchRules( doc: SavedObjectUnsanitizedDoc ): SavedObjectUnsanitizedDoc { @@ -957,8 +1006,8 @@ function removeInternalTags( } function pipeMigrations(...migrations: AlertMigration[]): AlertMigration { - return (doc: SavedObjectUnsanitizedDoc) => - migrations.reduce((migratedDoc, nextMigration) => nextMigration(migratedDoc), doc); + return (doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext) => + migrations.reduce((migratedDoc, nextMigration) => nextMigration(migratedDoc, context), doc); } function mapSearchSourceMigrationFunc( diff --git a/x-pack/plugins/alerting/server/task_runner/schedule_actions_for_alerts.test.ts b/x-pack/plugins/alerting/server/task_runner/schedule_actions_for_alerts.test.ts new file mode 100644 index 0000000000000..cdd16289c7a1a --- /dev/null +++ b/x-pack/plugins/alerting/server/task_runner/schedule_actions_for_alerts.test.ts @@ -0,0 +1,207 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { RuleRunMetricsStore } from '../lib/rule_run_metrics_store'; +import { RecoveredActionGroup } from '../types'; +import { RULE_NAME } from './fixtures'; +import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { scheduleActionsForAlerts } from './schedule_actions_for_alerts'; +import { Alert } from '../alert'; +import { AlertInstanceState, AlertInstanceContext, DefaultActionGroupId } from '../../common'; +import sinon from 'sinon'; + +describe('Schedule Actions For Alerts', () => { + const ruleRunMetricsStore = new RuleRunMetricsStore(); + const executionHandler = jest.fn(); + const recoveryActionGroup = RecoveredActionGroup; + const mutedAlertIdsSet = new Set('2'); + const logger: ReturnType = + loggingSystemMock.createLogger(); + const notifyWhen = 'onActiveAlert'; + const throttle = null; + let clock: sinon.SinonFakeTimers; + + beforeEach(() => { + jest.resetAllMocks(); + clock.reset(); + }); + beforeAll(() => { + clock = sinon.useFakeTimers(); + }); + afterAll(() => clock.restore()); + + test('schedules alerts with executable actions', async () => { + const alert = new Alert('1', { + state: { test: true }, + meta: { + lastScheduledActions: { + date: new Date(), + group: 'default', + }, + }, + }); + alert.scheduleActions('default'); + const alerts = { '1': alert }; + const recoveredAlerts = {}; + + await scheduleActionsForAlerts({ + activeAlerts: alerts, + recoveryActionGroup, + recoveredAlerts, + executionHandler, + mutedAlertIdsSet, + logger, + ruleLabel: RULE_NAME, + ruleRunMetricsStore, + throttle, + notifyWhen, + }); + + expect(executionHandler).toBeCalledWith({ + actionGroup: 'default', + context: {}, + state: { test: true }, + alertId: '1', + ruleRunMetricsStore, + }); + }); + + test('schedules alerts with recovered actions', async () => { + const alert = new Alert('1', { + state: { test: true }, + meta: { + lastScheduledActions: { + date: new Date(), + group: 'default', + }, + }, + }); + const alerts = {}; + const recoveredAlerts = { '1': alert }; + + await scheduleActionsForAlerts({ + activeAlerts: alerts, + recoveryActionGroup, + recoveredAlerts, + executionHandler, + mutedAlertIdsSet, + logger, + ruleLabel: RULE_NAME, + ruleRunMetricsStore, + throttle, + notifyWhen, + }); + + expect(executionHandler).toHaveBeenNthCalledWith(1, { + actionGroup: 'recovered', + context: {}, + state: {}, + alertId: '1', + ruleRunMetricsStore, + }); + }); + + test('does not schedule alerts with recovered actions that are muted', async () => { + const alert = new Alert('2', { + state: { test: true }, + meta: { + lastScheduledActions: { + date: new Date(), + group: 'default', + }, + }, + }); + const alerts = {}; + const recoveredAlerts = { '2': alert }; + + await scheduleActionsForAlerts({ + activeAlerts: alerts, + recoveryActionGroup, + recoveredAlerts, + executionHandler, + mutedAlertIdsSet, + logger, + ruleLabel: RULE_NAME, + ruleRunMetricsStore, + throttle, + notifyWhen, + }); + + expect(executionHandler).not.toBeCalled(); + expect(logger.debug).nthCalledWith( + 1, + `skipping scheduling of actions for '2' in rule ${RULE_NAME}: instance is muted` + ); + }); + + test('does not schedule active alerts that are throttled', async () => { + const alert = new Alert('1', { + state: { test: true }, + meta: { + lastScheduledActions: { + date: new Date(), + group: 'default', + }, + }, + }); + clock.tick(30000); + alert.scheduleActions('default'); + const alerts = { '1': alert }; + const recoveredAlerts = {}; + + await scheduleActionsForAlerts({ + activeAlerts: alerts, + recoveryActionGroup, + recoveredAlerts, + executionHandler, + mutedAlertIdsSet, + logger, + ruleLabel: RULE_NAME, + ruleRunMetricsStore, + throttle: '1m', + notifyWhen, + }); + expect(executionHandler).not.toBeCalled(); + expect(logger.debug).nthCalledWith( + 1, + `skipping scheduling of actions for '1' in rule ${RULE_NAME}: rule is throttled` + ); + }); + + test('does not schedule active alerts that are muted', async () => { + const alert = new Alert('2', { + state: { test: true }, + meta: { + lastScheduledActions: { + date: new Date(), + group: 'default', + }, + }, + }); + const alerts = { '2': alert }; + const recoveredAlerts = {}; + + await scheduleActionsForAlerts({ + activeAlerts: alerts, + recoveryActionGroup, + recoveredAlerts, + executionHandler, + mutedAlertIdsSet, + logger, + ruleLabel: RULE_NAME, + ruleRunMetricsStore, + throttle, + notifyWhen, + }); + + expect(executionHandler).not.toBeCalled(); + expect(logger.debug).nthCalledWith( + 1, + `skipping scheduling of actions for '2' in rule ${RULE_NAME}: rule is muted` + ); + }); +}); diff --git a/x-pack/plugins/alerting/server/task_runner/schedule_actions_for_alerts.ts b/x-pack/plugins/alerting/server/task_runner/schedule_actions_for_alerts.ts new file mode 100644 index 0000000000000..e0cc6d5b81c3a --- /dev/null +++ b/x-pack/plugins/alerting/server/task_runner/schedule_actions_for_alerts.ts @@ -0,0 +1,147 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Logger } from '@kbn/core/server'; +import { ExecutionHandler } from './create_execution_handler'; +import { ScheduleActionsForAlertsParams } from './types'; +import { AlertInstanceState, AlertInstanceContext } from '../types'; +import { Alert } from '../alert'; +import { RuleRunMetricsStore } from '../lib/rule_run_metrics_store'; + +export async function scheduleActionsForAlerts< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext, + ActionGroupIds extends string, + RecoveryActionGroupId extends string +>( + params: ScheduleActionsForAlertsParams< + InstanceState, + InstanceContext, + ActionGroupIds, + RecoveryActionGroupId + > +): Promise { + const { + logger, + activeAlerts, + recoveryActionGroup, + recoveredAlerts, + executionHandler, + mutedAlertIdsSet, + ruleLabel, + ruleRunMetricsStore, + throttle, + notifyWhen, + } = params; + // execute alerts with executable actions + for (const [alertId, alert] of Object.entries(activeAlerts)) { + const executeAction: boolean = shouldExecuteAction( + alertId, + alert, + mutedAlertIdsSet, + ruleLabel, + logger, + throttle, + notifyWhen + ); + if (executeAction && alert.hasScheduledActions()) { + const { actionGroup, subgroup: actionSubgroup, state } = alert.getScheduledActionOptions()!; + await executeAlert( + alertId, + alert, + executionHandler, + ruleRunMetricsStore, + actionGroup, + state, + actionSubgroup + ); + } + } + + // execute recovered alerts + for (const alertId of Object.keys(recoveredAlerts)) { + if (mutedAlertIdsSet.has(alertId)) { + logger.debug( + `skipping scheduling of actions for '${alertId}' in rule ${ruleLabel}: instance is muted` + ); + } else { + const alert = recoveredAlerts[alertId]; + await executeAlert( + alertId, + alert, + executionHandler, + ruleRunMetricsStore, + recoveryActionGroup.id, + {} as InstanceState + ); + alert.scheduleActions(recoveryActionGroup.id); + } + } +} + +async function executeAlert< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext, + ActionGroupIds extends string, + RecoveryActionGroupId extends string +>( + alertId: string, + alert: Alert, + executionHandler: ExecutionHandler, + ruleRunMetricsStore: RuleRunMetricsStore, + actionGroup: ActionGroupIds | RecoveryActionGroupId, + state: InstanceState, + actionSubgroup?: string +) { + alert.updateLastScheduledActions(actionGroup, actionSubgroup); + alert.unscheduleActions(); + return executionHandler({ + actionGroup, + actionSubgroup, + context: alert.getContext(), + state, + alertId, + ruleRunMetricsStore, + }); +} + +function shouldExecuteAction< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext, + ActionGroupIds extends string +>( + alertId: string, + alert: Alert, + mutedAlertIdsSet: Set, + ruleLabel: string, + logger: Logger, + throttle: string | null, + notifyWhen: string | null +) { + const throttled = alert.isThrottled(throttle); + const muted = mutedAlertIdsSet.has(alertId); + let executeAction = true; + + if (throttled || muted) { + executeAction = false; + logger.debug( + `skipping scheduling of actions for '${alertId}' in rule ${ruleLabel}: rule is ${ + muted ? 'muted' : 'throttled' + }` + ); + } else if ( + notifyWhen === 'onActionGroupChange' && + !alert.scheduledActionGroupOrSubgroupHasChanged() + ) { + executeAction = false; + logger.debug( + `skipping scheduling of actions for '${alertId}' in rule ${ruleLabel}: alert is active but action group has not changed` + ); + } + + return executeAction; +} diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index d5aaacbacc16a..340ef186b2730 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -13,7 +13,7 @@ import { KibanaRequest, Logger } from '@kbn/core/server'; import { ConcreteTaskInstance, throwUnrecoverableError } from '@kbn/task-manager-plugin/server'; import { nanosToMillis } from '@kbn/event-log-plugin/server'; import { TaskRunnerContext } from './task_runner_factory'; -import { createExecutionHandler, ExecutionHandler } from './create_execution_handler'; +import { createExecutionHandler } from './create_execution_handler'; import { Alert, createAlertFactory } from '../alert'; import { ElasticsearchError, @@ -58,7 +58,6 @@ import { InMemoryMetrics, IN_MEMORY_METRICS } from '../monitoring'; import { RuleTaskInstance, RuleTaskRunResult, - ScheduleActionsForRecoveredAlertsParams, RuleRunResult, RuleTaskStateAndMetrics, } from './types'; @@ -70,6 +69,7 @@ import { AlertingEventLogger } from '../lib/alerting_event_logger/alerting_event import { SearchMetrics } from '../lib/types'; import { loadRule } from './rule_loader'; import { logAlerts } from './log_alerts'; +import { scheduleActionsForAlerts } from './schedule_actions_for_alerts'; const FALLBACK_RETRY_INTERVAL = '5m'; const CONNECTIVITY_RETRY_INTERVAL = '5m'; @@ -224,34 +224,6 @@ export class TaskRunner< } } - private async executeAlert( - alertId: string, - alert: Alert, - executionHandler: ExecutionHandler, - ruleRunMetricsStore: RuleRunMetricsStore - ) { - if (alert.hasScheduledActions()) { - const { - actionGroup, - subgroup: actionSubgroup, - context, - state, - } = alert.getScheduledActionOptions()!; - alert.updateLastScheduledActions(actionGroup, actionSubgroup); - alert.unscheduleActions(); - return executionHandler({ - actionGroup, - actionSubgroup, - context, - state, - alertId, - ruleRunMetricsStore, - }); - } - - return Promise.resolve(); - } - private async executeRule( fakeRequest: KibanaRequest, rulesClient: RulesClientApi, @@ -468,41 +440,8 @@ export class TaskRunner< if (!ruleIsSnoozed && this.shouldLogAndScheduleActionsForAlerts()) { const mutedAlertIdsSet = new Set(mutedInstanceIds); - const alertsWithExecutableActions = Object.entries(activeAlerts).filter( - ([alertName, alert]: [string, Alert]) => { - const throttled = alert.isThrottled(throttle); - const muted = mutedAlertIdsSet.has(alertName); - let shouldExecuteAction = true; - - if (throttled || muted) { - shouldExecuteAction = false; - this.logger.debug( - `skipping scheduling of actions for '${alertName}' in rule ${ruleLabel}: rule is ${ - muted ? 'muted' : 'throttled' - }` - ); - } else if ( - notifyWhen === 'onActionGroupChange' && - !alert.scheduledActionGroupOrSubgroupHasChanged() - ) { - shouldExecuteAction = false; - this.logger.debug( - `skipping scheduling of actions for '${alertName}' in rule ${ruleLabel}: alert is active but action group has not changed` - ); - } - - return shouldExecuteAction; - } - ); - - await Promise.all( - alertsWithExecutableActions.map( - ([alertId, alert]: [string, Alert]) => - this.executeAlert(alertId, alert, executionHandler, ruleRunMetricsStore) - ) - ); - - await scheduleActionsForRecoveredAlerts({ + await scheduleActionsForAlerts({ + activeAlerts, recoveryActionGroup: this.ruleType.recoveryActionGroup, recoveredAlerts, executionHandler, @@ -510,6 +449,8 @@ export class TaskRunner< logger: this.logger, ruleLabel, ruleRunMetricsStore, + throttle, + notifyWhen, }); } else { if (ruleIsSnoozed) { @@ -809,49 +750,6 @@ export class TaskRunner< } } -async function scheduleActionsForRecoveredAlerts< - InstanceState extends AlertInstanceState, - InstanceContext extends AlertInstanceContext, - RecoveryActionGroupId extends string ->( - params: ScheduleActionsForRecoveredAlertsParams< - InstanceState, - InstanceContext, - RecoveryActionGroupId - > -): Promise { - const { - logger, - recoveryActionGroup, - recoveredAlerts, - executionHandler, - mutedAlertIdsSet, - ruleLabel, - ruleRunMetricsStore, - } = params; - const recoveredIds = Object.keys(recoveredAlerts); - - for (const id of recoveredIds) { - if (mutedAlertIdsSet.has(id)) { - logger.debug( - `skipping scheduling of actions for '${id}' in rule ${ruleLabel}: instance is muted` - ); - } else { - const alert = recoveredAlerts[id]; - alert.updateLastScheduledActions(recoveryActionGroup.id); - alert.unscheduleActions(); - await executionHandler({ - actionGroup: recoveryActionGroup.id, - context: alert.getContext(), - state: {}, - alertId: id, - ruleRunMetricsStore, - }); - alert.scheduleActions(recoveryActionGroup.id); - } - } -} - /** * If an error is thrown, wrap it in an RuleTaskRunResult * so that we can treat each field independantly diff --git a/x-pack/plugins/alerting/server/task_runner/types.ts b/x-pack/plugins/alerting/server/task_runner/types.ts index 839e0ca229052..9538b3e20a9d9 100644 --- a/x-pack/plugins/alerting/server/task_runner/types.ts +++ b/x-pack/plugins/alerting/server/task_runner/types.ts @@ -48,18 +48,22 @@ export interface RuleTaskInstance extends ConcreteTaskInstance { state: RuleTaskState; } -export interface ScheduleActionsForRecoveredAlertsParams< +export interface ScheduleActionsForAlertsParams< InstanceState extends AlertInstanceState, InstanceContext extends AlertInstanceContext, + ActionGroupIds extends string, RecoveryActionGroupId extends string > { logger: Logger; recoveryActionGroup: ActionGroup; recoveredAlerts: Record>; - executionHandler: ExecutionHandler; + executionHandler: ExecutionHandler; mutedAlertIdsSet: Set; ruleLabel: string; ruleRunMetricsStore: RuleRunMetricsStore; + activeAlerts: Record>; + throttle: string | null; + notifyWhen: string | null; } // / ExecutionHandler diff --git a/x-pack/plugins/apm/ftr_e2e/cypress.json b/x-pack/plugins/apm/ftr_e2e/cypress.json index 1791baaa5aae4..848a10efed668 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress.json +++ b/x-pack/plugins/apm/ftr_e2e/cypress.json @@ -6,11 +6,14 @@ "screenshotsFolder": "./cypress/screenshots", "supportFile": "./cypress/support/index.ts", "videosFolder": "./cypress/videos", + "requestTimeout": 10000, + "responseTimeout": 40000, "defaultCommandTimeout": 30000, "execTimeout": 120000, "pageLoadTimeout": 120000, "viewportHeight": 900, "viewportWidth": 1440, "video": false, - "screenshotOnRunFailure": false + "screenshotOnRunFailure": false, + "experimentalSessionAndOrigin": true } diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/feature_flag/comparison.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/feature_flag/comparison.spec.ts index 1a58eb1ca4fda..d1159efd0fc90 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/feature_flag/comparison.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/feature_flag/comparison.spec.ts @@ -10,9 +10,9 @@ import { opbeans } from '../../../fixtures/synthtrace/opbeans'; const start = '2021-10-10T00:00:00.000Z'; const end = '2021-10-10T00:15:00.000Z'; -describe.skip('Comparison feature flag', () => { - before(async () => { - await synthtrace.index( +describe('Comparison feature flag', () => { + before(() => { + synthtrace.index( opbeans({ from: new Date(start).getTime(), to: new Date(end).getTime(), @@ -20,29 +20,33 @@ describe.skip('Comparison feature flag', () => { ); }); - after(async () => { - await synthtrace.clean(); + after(() => { + synthtrace.clean(); }); describe('when comparison feature is enabled', () => { beforeEach(() => { cy.loginAsEditorUser(); + + cy.updateAdvancedSettings({ + 'observability:enableComparisonByDefault': true, + }); }); it('shows the comparison feature enabled in services overview', () => { - cy.visit('/app/apm/services'); + cy.visitKibana('/app/apm/services'); cy.get('input[type="checkbox"]#comparison').should('be.checked'); cy.get('[data-test-subj="comparisonSelect"]').should('not.be.disabled'); }); - it('shows the comparison feature enabled in services overview', () => { - cy.visit('/app/apm/dependencies'); + it('shows the comparison feature enabled in dependencies overview', () => { + cy.visitKibana('/app/apm/dependencies'); cy.get('input[type="checkbox"]#comparison').should('be.checked'); cy.get('[data-test-subj="comparisonSelect"]').should('not.be.disabled'); }); it('shows the comparison feature disabled in service map overview page', () => { - cy.visit('/app/apm/service-map'); + cy.visitKibana('/app/apm/service-map'); cy.get('input[type="checkbox"]#comparison').should('be.checked'); cy.get('[data-test-subj="comparisonSelect"]').should('not.be.disabled'); }); @@ -50,11 +54,11 @@ describe.skip('Comparison feature flag', () => { describe('when comparison feature is disabled', () => { beforeEach(() => { - cy.loginAsEditorUser().then(() => { - // Disables comparison feature on advanced settings - cy.updateAdvancedSettings({ - 'observability:enableComparisonByDefault': false, - }); + cy.loginAsEditorUser(); + + // Disables comparison feature on advanced settings + cy.updateAdvancedSettings({ + 'observability:enableComparisonByDefault': false, }); }); @@ -65,7 +69,7 @@ describe.skip('Comparison feature flag', () => { }); it('shows the comparison feature disabled in services overview', () => { - cy.visit('/app/apm/services'); + cy.visitKibana('/app/apm/services'); cy.get('input[type="checkbox"]#comparison').should('not.be.checked'); cy.get('[data-test-subj="comparisonSelect"]').should('be.disabled'); }); @@ -74,14 +78,14 @@ describe.skip('Comparison feature flag', () => { cy.intercept('GET', '/internal/apm/dependencies/top_dependencies?*').as( 'topDependenciesRequest' ); - cy.visit('/app/apm/dependencies'); - cy.wait('@topDependenciesRequest', { requestTimeout: 10000 }); + cy.visitKibana('/app/apm/dependencies'); + cy.wait('@topDependenciesRequest'); cy.get('input[type="checkbox"]#comparison').should('not.be.checked'); cy.get('[data-test-subj="comparisonSelect"]').should('be.disabled'); }); it('shows the comparison feature disabled in service map overview page', () => { - cy.visit('/app/apm/service-map'); + cy.visitKibana('/app/apm/service-map'); cy.get('input[type="checkbox"]#comparison').should('not.be.checked'); cy.get('[data-test-subj="comparisonSelect"]').should('be.disabled'); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/infrastructure/infrastructure_page.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/infrastructure/infrastructure_page.spec.ts index 46d654ef2a4aa..439b95a796b71 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/infrastructure/infrastructure_page.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/infrastructure/infrastructure_page.spec.ts @@ -27,9 +27,9 @@ const nodeServiceInfraPageHref = url.format({ query: { rangeFrom: start, rangeTo: end }, }); -describe.skip('Infrastructure page', () => { - before(async () => { - await synthtrace.index( +describe('Infrastructure page', () => { + before(() => { + synthtrace.index( generateData({ from: new Date(start).getTime(), to: new Date(end).getTime(), @@ -37,8 +37,8 @@ describe.skip('Infrastructure page', () => { ); }); - after(async () => { - await synthtrace.clean(); + after(() => { + synthtrace.clean(); }); beforeEach(() => { @@ -47,7 +47,7 @@ describe.skip('Infrastructure page', () => { describe('when data is loaded', () => { it('has no detectable a11y violations on load', () => { - cy.visit(goServiceInfraPageHref); + cy.visitKibana(goServiceInfraPageHref); cy.contains('Infrastructure'); // set skipFailures to true to not fail the test when there are accessibility failures checkA11y({ skipFailures: true }); @@ -55,7 +55,7 @@ describe.skip('Infrastructure page', () => { describe('when container ids, pod names and host names are returned by the api call', () => { it('shows all tabs', () => { - cy.visit(goServiceInfraPageHref); + cy.visitKibana(goServiceInfraPageHref); cy.contains('Containers'); cy.contains('Pods'); cy.contains('Hosts'); @@ -64,14 +64,14 @@ describe.skip('Infrastructure page', () => { describe('when only host names are returned by the api call', () => { it('shows only Hosts tab', () => { - cy.visit(javaServiceInfraPageHref); + cy.visitKibana(javaServiceInfraPageHref); cy.contains('Hosts'); }); }); describe('when none infrastructure attributes are returned by the api call', () => { it('shows no data message', () => { - cy.visit(nodeServiceInfraPageHref); + cy.visitKibana(nodeServiceInfraPageHref); cy.contains('No results match your search criteria.'); }); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/integration_settings/integration_policy.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/integration_settings/integration_policy.spec.ts index 655376eb3eb63..7b97813cb1219 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/integration_settings/integration_policy.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/integration_settings/integration_policy.spec.ts @@ -57,7 +57,7 @@ describe.skip('when navigating to integration page', () => { const integrationsPath = '/app/integrations/browse'; cy.loginAsEditorUser(); - cy.visit(integrationsPath); + cy.visitKibana(integrationsPath); // open integration policy form cy.get('[data-test-subj="integration-card:epr:apm:featured').click(); @@ -79,17 +79,17 @@ describe.skip('when navigating to integration page', () => { }); it('should display Tail-based section on latest version', () => { - cy.visit('/app/fleet/integrations/apm/add-integration'); + cy.visitKibana('/app/fleet/integrations/apm/add-integration'); cy.contains('Tail-based sampling').should('exist'); }); it('should hide Tail-based section for 8.0.0 apm package', () => { - cy.visit('/app/fleet/integrations/apm-8.0.0/add-integration'); + cy.visitKibana('/app/fleet/integrations/apm-8.0.0/add-integration'); cy.contains('Tail-based sampling').should('not.exist'); }); it('should Display Debug section', () => { - cy.visit('/app/fleet/integrations/apm-8.0.0/add-integration'); + cy.visitKibana('/app/fleet/integrations/apm-8.0.0/add-integration'); cy.contains('Debug settings').should('exist'); }); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/no_data_screen.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/no_data_screen.ts index c7f33301a5dfc..ae08bc1ea9b63 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/no_data_screen.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/no_data_screen.ts @@ -5,60 +5,55 @@ * 2.0. */ -const apmIndicesSaveURL = '/internal/apm/settings/apm-indices/save'; - describe('No data screen', () => { describe('bypass no data screen on settings pages', () => { - beforeEach(() => { - cy.loginAsEditorUser(); - }); - before(() => { - // Change default indices - cy.request({ - url: apmIndicesSaveURL, - method: 'POST', - body: { - sourcemap: 'foo-*', - error: 'foo-*', - onboarding: 'foo-*', - span: 'foo-*', - transaction: 'foo-*', - metric: 'foo-*', - }, - headers: { - 'kbn-xsrf': true, - }, - auth: { user: 'editor', pass: 'changeme' }, + // Change indices + setApmIndices({ + sourcemap: 'foo-*', + error: 'foo-*', + onboarding: 'foo-*', + span: 'foo-*', + transaction: 'foo-*', + metric: 'foo-*', }); }); + beforeEach(() => { + cy.loginAsEditorUser(); + }); + it('shows no data screen instead of service inventory', () => { - cy.visit('/app/apm/'); + cy.visitKibana('/app/apm/'); cy.contains('Welcome to Elastic Observability!'); }); + it('shows settings page', () => { - cy.visit('/app/apm/settings'); + cy.visitKibana('/app/apm/settings'); cy.contains('Welcome to Elastic Observability!').should('not.exist'); cy.get('h1').contains('Settings'); }); after(() => { // reset to default indices - cy.request({ - url: apmIndicesSaveURL, - method: 'POST', - body: { - sourcemap: '', - error: '', - onboarding: '', - span: '', - transaction: '', - metric: '', - }, - headers: { 'kbn-xsrf': true }, - auth: { user: 'editor', pass: 'changeme' }, + setApmIndices({ + sourcemap: '', + error: '', + onboarding: '', + span: '', + transaction: '', + metric: '', }); }); }); }); + +function setApmIndices(body: Record) { + cy.request({ + url: '/internal/apm/settings/apm-indices/save', + method: 'POST', + body, + headers: { 'kbn-xsrf': true }, + auth: { user: 'editor', pass: 'changeme' }, + }); +} diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/rules/error_count.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/rules/error_count.spec.ts index b145369535225..ec01fc2df2da3 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/rules/error_count.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/rules/error_count.spec.ts @@ -6,10 +6,12 @@ */ function deleteAllRules() { + cy.log('Delete all rules'); cy.request({ log: false, method: 'GET', url: '/api/alerting/rules/_find', + auth: { user: 'editor', pass: 'changeme' }, }).then(({ body }) => { if (body.data.length > 0) { cy.log(`Deleting rules`); @@ -21,12 +23,21 @@ function deleteAllRules() { log: false, method: 'DELETE', url: `/api/alerting/rule/${id}`, + auth: { user: 'editor', pass: 'changeme' }, }); }); }); } describe('Rules', () => { + beforeEach(() => { + deleteAllRules(); + }); + + after(() => { + deleteAllRules(); + }); + describe('Error count', () => { const ruleName = 'Error count threshold'; const comboBoxInputSelector = @@ -36,18 +47,11 @@ describe('Rules', () => { describe('when created from APM', () => { describe('when created from Service Inventory', () => { - before(() => { + it('creates a rule', () => { cy.loginAsEditorUser(); - deleteAllRules(); - }); - - after(() => { - deleteAllRules(); - }); - it('creates a rule', () => { // Create a rule in APM - cy.visit('/app/apm/services'); + cy.visitKibana('/app/apm/services'); cy.contains('Alerts and rules').click(); cy.contains('Create error count rule').click(); @@ -67,18 +71,13 @@ describe('Rules', () => { }); describe('when created from Stack management', () => { - before(() => { + it('creates a rule', () => { cy.loginAsEditorUser(); - deleteAllRules(); - }); - after(() => { - deleteAllRules(); - }); - - it('creates a rule', () => { // Go to stack management - cy.visit('/app/management/insightsAndAlerting/triggersActions/rules'); + cy.visitKibana( + '/app/management/insightsAndAlerting/triggersActions/rules' + ); // Create a rule cy.contains('button', 'Create rule').click(); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/settings/agent_configurations.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/settings/agent_configurations.spec.ts index a2f5e055e80a8..23154492c9f44 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/settings/agent_configurations.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/settings/agent_configurations.spec.ts @@ -55,10 +55,10 @@ function generateData({ } describe('Agent configuration', () => { - before(async () => { + before(() => { const { rangeFrom, rangeTo } = timeRange; - await synthtrace.index( + synthtrace.index( generateData({ from: new Date(rangeFrom).getTime(), to: new Date(rangeTo).getTime(), @@ -67,13 +67,13 @@ describe('Agent configuration', () => { ); }); - after(async () => { - await synthtrace.clean(); + after(() => { + synthtrace.clean(); }); beforeEach(() => { cy.loginAsEditorUser(); - cy.visit(agentConfigHref); + cy.visitKibana(agentConfigHref); }); it('persists service enviroment when clicking on edit button', () => { diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/settings/custom_links.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/settings/custom_links.spec.ts index e5c409cb6da0d..53dbb82c1c6ae 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/settings/custom_links.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/settings/custom_links.spec.ts @@ -13,13 +13,13 @@ describe('Custom links', () => { }); it('shows empty message and create button', () => { - cy.visit(basePath); + cy.visitKibana(basePath); cy.contains('No links found'); cy.contains('Create custom link'); }); it('creates custom link', () => { - cy.visit(basePath); + cy.visitKibana(basePath); const emptyPrompt = cy.get('[data-test-subj="customLinksEmptyPrompt"]'); cy.contains('Create custom link').click(); cy.contains('Create link'); @@ -36,7 +36,7 @@ describe('Custom links', () => { }); it('clears filter values when field is selected', () => { - cy.visit(basePath); + cy.visitKibana(basePath); cy.contains('Create custom link').click(); cy.get('[data-test-subj="filter-0"]').select('service.name'); cy.get( diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/deep_links.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/deep_links.spec.ts index 76c43ef03f332..cfcabe85b5b2a 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/deep_links.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/deep_links.spec.ts @@ -6,11 +6,11 @@ */ describe('APM deep links', () => { - before(() => { + beforeEach(() => { cy.loginAsViewerUser(); }); it('navigates to apm links on search elastic', () => { - cy.visit('/'); + cy.visitKibana('/'); cy.get('[data-test-subj="nav-search-input"]').type('APM'); cy.contains('APM'); cy.contains('APM / Services'); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/dependencies.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/dependencies.spec.ts index 8042a1a07f8a2..e3746489936ab 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/dependencies.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/dependencies.spec.ts @@ -17,8 +17,8 @@ const timeRange = { }; describe.skip('Dependencies', () => { - before(async () => { - await synthtrace.index( + before(() => { + synthtrace.index( opbeans({ from: new Date(start).getTime(), to: new Date(end).getTime(), @@ -26,8 +26,8 @@ describe.skip('Dependencies', () => { ); }); - after(async () => { - await synthtrace.clean(); + after(() => { + synthtrace.clean(); }); beforeEach(() => { @@ -36,7 +36,7 @@ describe.skip('Dependencies', () => { describe('top-level dependencies page', () => { it('has a list of dependencies and you can navigate to the page for one', () => { - cy.visit(`/app/apm/services?${new URLSearchParams(timeRange)}`); + cy.visitKibana(`/app/apm/services?${new URLSearchParams(timeRange)}`); cy.contains('nav a', 'Dependencies').click(); // `force: true` because Cypress says the element is 0x0 @@ -46,7 +46,7 @@ describe.skip('Dependencies', () => { }); it('has no detectable a11y violations on load', () => { - cy.visit( + cy.visitKibana( `/app/apm/services/opbeans-java/dependencies?${new URLSearchParams( timeRange )}` @@ -59,7 +59,7 @@ describe.skip('Dependencies', () => { describe.skip('dependency overview page', () => { it('shows dependency information and you can navigate to a page for an upstream service', () => { - cy.visit( + cy.visitKibana( `/app/apm/dependencies/overview?${new URLSearchParams({ ...timeRange, dependencyName: 'postgresql', @@ -76,7 +76,7 @@ describe.skip('Dependencies', () => { }); it('has no detectable a11y violations on load', () => { - cy.visit( + cy.visitKibana( `/app/apm/dependencies/overview?${new URLSearchParams({ ...timeRange, dependencyName: 'postgresql', @@ -90,7 +90,7 @@ describe.skip('Dependencies', () => { describe('service overview page', () => { it('shows dependency information and you can navigate to a page for a dependency', () => { - cy.visit( + cy.visitKibana( `/app/apm/services/opbeans-java/overview?${new URLSearchParams( timeRange )}` @@ -104,7 +104,7 @@ describe.skip('Dependencies', () => { describe('service dependencies tab', () => { it('shows dependency information and you can navigate to a page for a dependency', () => { - cy.visit( + cy.visitKibana( `/app/apm/services/opbeans-java/overview?${new URLSearchParams( timeRange )}` diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/errors/error_details.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/errors/error_details.spec.ts index 59c0d87029517..28b3068503644 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/errors/error_details.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/errors/error_details.spec.ts @@ -27,8 +27,8 @@ describe('Error details', () => { }); describe('when data is loaded', () => { - before(async () => { - await synthtrace.index( + before(() => { + synthtrace.index( generateData({ from: new Date(start).getTime(), to: new Date(end).getTime(), @@ -36,12 +36,12 @@ describe('Error details', () => { ); }); - after(async () => { - await synthtrace.clean(); + after(() => { + synthtrace.clean(); }); it('has no detectable a11y violations on load', () => { - cy.visit(errorDetailsPageHref); + cy.visitKibana(errorDetailsPageHref); cy.contains('Error group 00000'); // set skipFailures to true to not fail the test when there are accessibility failures checkA11y({ skipFailures: true }); @@ -49,7 +49,7 @@ describe('Error details', () => { describe('when error has no occurrences', () => { it('shows an empty message', () => { - cy.visit( + cy.visitKibana( url.format({ pathname: '/app/apm/services/opbeans-java/errors/0000000000000000000000000Error%201', @@ -66,13 +66,13 @@ describe('Error details', () => { describe('when error has data', () => { it('shows errors distribution chart', () => { - cy.visit(errorDetailsPageHref); + cy.visitKibana(errorDetailsPageHref); cy.contains('Error group 00000'); cy.get('[data-test-subj="errorDistribution"]').contains('Occurrences'); }); it('shows top erroneous transactions table', () => { - cy.visit(errorDetailsPageHref); + cy.visitKibana(errorDetailsPageHref); cy.contains('Top 5 affected transactions'); cy.get('[data-test-subj="topErroneousTransactionsTable"]') .contains('a', 'GET /apple 🍎') @@ -81,14 +81,14 @@ describe('Error details', () => { }); it('shows a Stacktrace and Metadata tabs', () => { - cy.visit(errorDetailsPageHref); + cy.visitKibana(errorDetailsPageHref); cy.contains('button', 'Exception stack trace'); cy.contains('button', 'Metadata'); }); describe('when clicking on related transaction sample', () => { it('should redirects to the transaction details page', () => { - cy.visit(errorDetailsPageHref); + cy.visitKibana(errorDetailsPageHref); cy.contains('Error group 00000'); cy.contains('a', 'GET /apple 🍎').click(); cy.url().should('include', 'opbeans-java/transactions/view'); @@ -97,7 +97,7 @@ describe('Error details', () => { describe('when clicking on View x occurences in discover', () => { it.skip('should redirects the user to discover', () => { - cy.visit(errorDetailsPageHref); + cy.visitKibana(errorDetailsPageHref); cy.contains('View 1 occurrence in Discover').click(); cy.url().should('include', 'app/discover'); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/errors/errors_page.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/errors/errors_page.spec.ts index b000a3a8c2f3f..301b3384ee2eb 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/errors/errors_page.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/errors/errors_page.spec.ts @@ -29,8 +29,8 @@ describe('Errors page', () => { }); describe('when data is loaded', () => { - before(async () => { - await synthtrace.index( + before(() => { + synthtrace.index( generateData({ from: new Date(start).getTime(), to: new Date(end).getTime(), @@ -38,12 +38,12 @@ describe('Errors page', () => { ); }); - after(async () => { - await synthtrace.clean(); + after(() => { + synthtrace.clean(); }); it('has no detectable a11y violations on load', () => { - cy.visit(javaServiceErrorsPageHref); + cy.visitKibana(javaServiceErrorsPageHref); cy.contains('Error occurrences'); // set skipFailures to true to not fail the test when there are accessibility failures checkA11y({ skipFailures: true }); @@ -51,7 +51,7 @@ describe('Errors page', () => { describe('when service has no errors', () => { it('shows empty message', () => { - cy.visit(nodeServiceErrorsPageHref); + cy.visitKibana(nodeServiceErrorsPageHref); cy.contains('opbeans-node'); cy.contains('No errors found'); }); @@ -59,28 +59,28 @@ describe('Errors page', () => { describe('when service has errors', () => { it('shows errors distribution chart', () => { - cy.visit(javaServiceErrorsPageHref); + cy.visitKibana(javaServiceErrorsPageHref); cy.contains('Error occurrences'); }); it('shows failed transaction rate chart', () => { - cy.visit(javaServiceErrorsPageHref); + cy.visitKibana(javaServiceErrorsPageHref); cy.contains('Failed transaction rate'); }); it('errors table is populated', () => { - cy.visit(javaServiceErrorsPageHref); + cy.visitKibana(javaServiceErrorsPageHref); cy.contains('Error 0'); }); it('clicking on an error in the list navigates to error detail page', () => { - cy.visit(javaServiceErrorsPageHref); + cy.visitKibana(javaServiceErrorsPageHref); cy.contains('a', 'Error 1').click(); cy.contains('div', 'Error 1'); }); it('clicking on type adds a filter in the kuerybar', () => { - cy.visit(javaServiceErrorsPageHref); + cy.visitKibana(javaServiceErrorsPageHref); cy.get('[data-test-subj="headerFilterKuerybar"]') .invoke('val') .should('be.empty'); @@ -97,13 +97,13 @@ describe('Errors page', () => { }); it('sorts by ocurrences', () => { - cy.visit(javaServiceErrorsPageHref); + cy.visitKibana(javaServiceErrorsPageHref); cy.contains('span', 'Occurrences').click(); cy.url().should('include', '&sortField=occurrences&sortDirection=asc'); }); it('sorts by latest occurrences', () => { - cy.visit(javaServiceErrorsPageHref); + cy.visitKibana(javaServiceErrorsPageHref); cy.contains('span', 'Last seen').click(); cy.url().should('include', '&sortField=lastSeen&sortDirection=asc'); }); @@ -112,9 +112,8 @@ describe('Errors page', () => { }); describe('Check detailed statistics API with multiple errors', () => { - before(async () => { - cy.loginAsViewerUser(); - await synthtrace.index( + before(() => { + synthtrace.index( generateErrors({ from: new Date(start).getTime(), to: new Date(end).getTime(), @@ -123,8 +122,12 @@ describe('Check detailed statistics API with multiple errors', () => { ); }); - after(async () => { - await synthtrace.clean(); + beforeEach(() => { + cy.loginAsViewerUser(); + }); + + after(() => { + synthtrace.clean(); }); it('calls detailed API with visible items only', () => { @@ -136,7 +139,7 @@ describe('Check detailed statistics API with multiple errors', () => { 'POST', '/internal/apm/services/opbeans-java/errors/groups/detailed_statistics?*' ).as('errorsDetailedStatistics'); - cy.visit(`${javaServiceErrorsPageHref}&pageSize=10`); + cy.visitKibana(`${javaServiceErrorsPageHref}&pageSize=10`); cy.wait('@errorsMainStatistics'); cy.get('.euiPagination__list').children().should('have.length', 5); cy.wait('@errorsDetailedStatistics').then((payload) => { diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/home.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/home.spec.ts index 42ca9319b4ea3..be09ae3f06122 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/home.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/home.spec.ts @@ -24,8 +24,8 @@ const serviceInventoryHref = url.format({ }); describe.skip('Home page', () => { - before(async () => { - await synthtrace.index( + before(() => { + synthtrace.index( opbeans({ from: new Date(start).getTime(), to: new Date(end).getTime(), @@ -33,8 +33,8 @@ describe.skip('Home page', () => { ); }); - after(async () => { - await synthtrace.clean(); + after(() => { + synthtrace.clean(); }); beforeEach(() => { @@ -42,7 +42,7 @@ describe.skip('Home page', () => { }); it('Redirects to service page with comparisonEnabled, environment, rangeFrom, rangeTo and offset added to the URL', () => { - cy.visit('/app/apm'); + cy.visitKibana('/app/apm'); cy.url().should( 'include', @@ -51,7 +51,7 @@ describe.skip('Home page', () => { }); it('includes services with only metric documents', () => { - cy.visit( + cy.visitKibana( `${serviceInventoryHref}&kuery=not%20(processor.event%3A%22transaction%22)` ); cy.contains('opbeans-java'); @@ -60,7 +60,7 @@ describe.skip('Home page', () => { describe('navigations', () => { it('navigates to service overview page with transaction type', () => { - cy.visit(serviceInventoryHref); + cy.visitKibana(serviceInventoryHref); cy.contains('Services'); cy.contains('opbeans-rum').click({ force: true }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_inventory/header_filters/header_filters.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_inventory/header_filters/header_filters.spec.ts index 03972eb0d0f20..c4e87ac15fbe1 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_inventory/header_filters/header_filters.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_inventory/header_filters/header_filters.spec.ts @@ -20,8 +20,8 @@ const specialServiceName = 'service 1 / ? # [ ] @ ! $ & ( ) * + , ; = < > % {} | ^ ` <>'; describe('Service inventory - header filters', () => { - before(async () => { - await synthtrace.index( + before(() => { + synthtrace.index( generateData({ from: new Date(start).getTime(), to: new Date(end).getTime(), @@ -30,8 +30,8 @@ describe('Service inventory - header filters', () => { ); }); - after(async () => { - await synthtrace.clean(); + after(() => { + synthtrace.clean(); }); beforeEach(() => { @@ -40,7 +40,7 @@ describe('Service inventory - header filters', () => { describe('Filtering by kuerybar', () => { it('filters by service.name with special characters', () => { - cy.visit(serviceOverviewHref); + cy.visitKibana(serviceOverviewHref); cy.contains('Services'); cy.contains('opbeans-node'); cy.contains('service 1'); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_inventory/service_inventory.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_inventory/service_inventory.spec.ts index 6575d015abde2..5b98284cdf52a 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_inventory/service_inventory.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_inventory/service_inventory.spec.ts @@ -44,12 +44,9 @@ const mainAliasNames = mainApiRequestsToIntercept.map( ); describe('When navigating to the service inventory', () => { - before(async () => { - cy.loginAsViewerUser(); - cy.visit(serviceInventoryHref); - + before(() => { const { rangeFrom, rangeTo } = timeRange; - await synthtrace.index( + synthtrace.index( opbeans({ from: new Date(rangeFrom).getTime(), to: new Date(rangeTo).getTime(), @@ -57,8 +54,13 @@ describe('When navigating to the service inventory', () => { ); }); - after(async () => { - await synthtrace.clean(); + beforeEach(() => { + cy.loginAsViewerUser(); + cy.visitKibana(serviceInventoryHref); + }); + + after(() => { + synthtrace.clean(); }); it('has no detectable a11y violations on load', () => { @@ -92,7 +94,7 @@ describe('When navigating to the service inventory', () => { ); cy.loginAsViewerUser(); - cy.visit(serviceInventoryHref); + cy.visitKibana(serviceInventoryHref); }); it('with the correct environment when changing the environment', () => { @@ -135,11 +137,9 @@ describe('When navigating to the service inventory', () => { }); describe('Check detailed statistics API with multiple services', () => { - before(async () => { - cy.loginAsViewerUser(); + before(() => { const { rangeFrom, rangeTo } = timeRange; - - await synthtrace.index( + synthtrace.index( generateMultipleServicesData({ from: new Date(rangeFrom).getTime(), to: new Date(rangeTo).getTime(), @@ -147,8 +147,12 @@ describe('Check detailed statistics API with multiple services', () => { ); }); - after(async () => { - await synthtrace.clean(); + beforeEach(() => { + cy.loginAsViewerUser(); + }); + + after(() => { + synthtrace.clean(); }); it('calls detailed API with visible items only', () => { @@ -157,7 +161,7 @@ describe('Check detailed statistics API with multiple services', () => { ); cy.intercept('GET', '/internal/apm/services?*').as('mainStatisticsRequest'); - cy.visit( + cy.visitKibana( `${serviceInventoryHref}&pageSize=10&sortField=serviceName&sortDirection=asc` ); cy.wait('@mainStatisticsRequest'); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/aws_lambda/aws_lamba.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/aws_lambda/aws_lamba.spec.ts index a2674d056e317..a70620ee18f82 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/aws_lambda/aws_lamba.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/aws_lambda/aws_lamba.spec.ts @@ -23,8 +23,8 @@ const apiToIntercept = { }; describe('Service overview - aws lambda', () => { - before(async () => { - await synthtrace.index( + before(() => { + synthtrace.index( generateData({ start: new Date(start).getTime(), end: new Date(end).getTime(), @@ -32,19 +32,16 @@ describe('Service overview - aws lambda', () => { ); }); - after(async () => { - await synthtrace.clean(); - }); - - beforeEach(() => { - cy.loginAsViewerUser(); + after(() => { + synthtrace.clean(); }); it('displays a cold start rate chart and not a transaction breakdown chart', () => { const { endpoint, name } = apiToIntercept; - cy.intercept('GET', endpoint).as(name); - cy.visit(serviceOverviewHref); + + cy.loginAsViewerUser(); + cy.visitKibana(serviceOverviewHref); cy.wait(`@${name}`); cy.contains('Cold start rate'); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/errors_table.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/errors_table.spec.ts index 8d7cfd4e65288..b175eb0430ed4 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/errors_table.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/errors_table.spec.ts @@ -18,8 +18,8 @@ const serviceOverviewHref = url.format({ }); describe('Errors table', () => { - before(async () => { - await synthtrace.index( + before(() => { + synthtrace.index( opbeans({ from: new Date(start).getTime(), to: new Date(end).getTime(), @@ -27,8 +27,8 @@ describe('Errors table', () => { ); }); - after(async () => { - await synthtrace.clean(); + after(() => { + synthtrace.clean(); }); beforeEach(() => { @@ -36,20 +36,20 @@ describe('Errors table', () => { }); it('errors table is populated', () => { - cy.visit(serviceOverviewHref); + cy.visitKibana(serviceOverviewHref); cy.contains('opbeans-java'); cy.contains('[MockError] Foo'); }); it('navigates to the errors page', () => { - cy.visit(serviceOverviewHref); + cy.visitKibana(serviceOverviewHref); cy.contains('opbeans-java'); cy.contains('a', 'View errors').click(); cy.url().should('include', '/opbeans-java/errors'); }); it('clicking on type adds a filter in the kuerybar and navigates to errors page', () => { - cy.visit(serviceOverviewHref); + cy.visitKibana(serviceOverviewHref); cy.get('[data-test-subj="headerFilterKuerybar"]') .invoke('val') .should('be.empty'); @@ -64,7 +64,7 @@ describe('Errors table', () => { }); it('navigates to error detail page', () => { - cy.visit(serviceOverviewHref); + cy.visitKibana(serviceOverviewHref); cy.contains('a', '[MockError] Foo').click(); cy.contains('div', 'Exception message'); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/header_filters.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/header_filters.spec.ts index 3b7b0c87a7f84..cf5102ee6e37e 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/header_filters.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/header_filters.spec.ts @@ -59,14 +59,14 @@ const apisToIntercept = [ ]; describe('Service overview - header filters', () => { - before(async () => { - await synthtrace.index( + before(() => { + synthtrace.index( opbeans({ from: new Date(start).getTime(), to: new Date(end).getTime() }) ); }); - after(async () => { - await synthtrace.clean(); + after(() => { + synthtrace.clean(); }); describe('Filtering by transaction type', () => { @@ -74,7 +74,7 @@ describe('Service overview - header filters', () => { cy.loginAsViewerUser(); }); it('changes url when selecting different value', () => { - cy.visit(serviceOverviewHref); + cy.visitKibana(serviceOverviewHref); cy.contains('opbeans-node'); cy.url().should('not.include', 'transactionType'); cy.get('[data-test-subj="headerFilterTransactionType"]').should( @@ -93,7 +93,7 @@ describe('Service overview - header filters', () => { apisToIntercept.map(({ endpoint, name }) => { cy.intercept('GET', endpoint).as(name); }); - cy.visit(serviceOverviewHref); + cy.visitKibana(serviceOverviewHref); cy.get('[data-test-subj="headerFilterTransactionType"]').should( 'have.value', 'request' @@ -122,7 +122,7 @@ describe('Service overview - header filters', () => { cy.loginAsViewerUser(); }); it('filters by transaction.name', () => { - cy.visit( + cy.visitKibana( url.format({ pathname: '/app/apm/services/opbeans-java/overview', query: { rangeFrom: start, rangeTo: end }, diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/instances_table.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/instances_table.spec.ts index 1fc83b3e2eb0a..c3b5f37c32acc 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/instances_table.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/instances_table.spec.ts @@ -41,19 +41,19 @@ describe('Instances table', () => { cy.loginAsViewerUser(); }); - // describe('when data is not loaded', () => { - // it('shows empty message', () => { - // cy.visit(serviceOverviewHref); - // cy.contains('opbeans-java'); - // cy.get('[data-test-subj="serviceInstancesTableContainer"]').contains( - // 'No items found' - // ); - // }); - // }); + describe.skip('when data is not loaded', () => { + it('shows empty message', () => { + cy.visitKibana(serviceOverviewHref); + cy.contains('opbeans-java'); + cy.get('[data-test-subj="serviceInstancesTableContainer"]').contains( + 'No items found' + ); + }); + }); describe('when data is loaded', () => { - before(async () => { - await synthtrace.index( + before(() => { + synthtrace.index( opbeans({ from: new Date(start).getTime(), to: new Date(end).getTime(), @@ -61,12 +61,12 @@ describe('Instances table', () => { ); }); - after(async () => { - await synthtrace.clean(); + after(() => { + synthtrace.clean(); }); it('has data in the table', () => { - cy.visit(serviceOverviewHref); + cy.visitKibana(serviceOverviewHref); cy.contains('opbeans-java'); cy.contains(serviceNodeName); }); @@ -75,7 +75,7 @@ describe('Instances table', () => { cy.intercept('GET', endpoint).as(name); }); - cy.visit(serviceOverviewHref); + cy.visitKibana(serviceOverviewHref); cy.contains('opbeans-java'); cy.wait('@instancesMainRequest'); @@ -96,7 +96,7 @@ describe('Instances table', () => { cy.intercept('GET', endpoint).as(name); }); - cy.visit(serviceOverviewHref); + cy.visitKibana(serviceOverviewHref); cy.contains('opbeans-java'); cy.wait('@instancesMainRequest'); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/service_overview.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/service_overview.spec.ts index f61ad0c0761c6..d73713290b4ef 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/service_overview.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/service_overview.spec.ts @@ -86,8 +86,8 @@ const aliasNamesWithComparison = apiRequestsToInterceptWithComparison.map( const aliasNames = [...aliasNamesNoComparison, ...aliasNamesWithComparison]; describe('Service Overview', () => { - before(async () => { - await synthtrace.index( + before(() => { + synthtrace.index( opbeans({ from: new Date(start).getTime(), to: new Date(end).getTime(), @@ -95,14 +95,14 @@ describe('Service Overview', () => { ); }); - after(async () => { - await synthtrace.clean(); + after(() => { + synthtrace.clean(); }); describe('renders', () => { - before(() => { + beforeEach(() => { cy.loginAsViewerUser(); - cy.visit(baseUrl); + cy.visitKibana(baseUrl); }); it('renders all components on the page', () => { @@ -122,7 +122,6 @@ describe('Service Overview', () => { describe('transactions', () => { beforeEach(() => { cy.loginAsViewerUser(); - cy.visit(baseUrl); }); it('persists transaction type selected when clicking on Transactions tab', () => { @@ -130,6 +129,9 @@ describe('Service Overview', () => { 'GET', '/internal/apm/services/opbeans-node/transaction_types?*' ).as('transactionTypesRequest'); + + cy.visitKibana(baseUrl); + cy.wait('@transactionTypesRequest'); cy.get('[data-test-subj="headerFilterTransactionType"]').should( @@ -148,11 +150,14 @@ describe('Service Overview', () => { ); }); - it.skip('persists transaction type selected when clicking on View Transactions link', () => { + it('persists transaction type selected when clicking on View Transactions link', () => { cy.intercept( 'GET', '/internal/apm/services/opbeans-node/transaction_types?*' ).as('transactionTypesRequest'); + + cy.visitKibana(baseUrl); + cy.wait('@transactionTypesRequest'); cy.get('[data-test-subj="headerFilterTransactionType"]').should( 'have.value', @@ -173,20 +178,20 @@ describe('Service Overview', () => { }); describe('when RUM service', () => { - before(() => { + it('hides dependency tab when RUM service', () => { cy.loginAsViewerUser(); - cy.visit( + + cy.intercept('GET', '/internal/apm/services/opbeans-rum/agent?*').as( + 'agentRequest' + ); + + cy.visitKibana( url.format({ pathname: '/app/apm/services/opbeans-rum/overview', query: { rangeFrom: start, rangeTo: end }, }) ); - }); - it('hides dependency tab when RUM service', () => { - cy.intercept('GET', '/internal/apm/services/opbeans-rum/agent?*').as( - 'agentRequest' - ); cy.contains('Overview'); cy.contains('Transactions'); cy.contains('Error'); @@ -204,17 +209,18 @@ describe('Service Overview', () => { describe('Calls APIs', () => { beforeEach(() => { cy.loginAsViewerUser(); - cy.visit(baseUrl); + apiRequestsToIntercept.map(({ endpoint, aliasName }) => { cy.intercept('GET', endpoint).as(aliasName); }); apiRequestsToInterceptWithComparison.map(({ endpoint, aliasName }) => { cy.intercept('GET', endpoint).as(aliasName); }); + cy.visitKibana(baseUrl); }); it.skip('with the correct environment when changing the environment', () => { - cy.wait(aliasNames, { requestTimeout: 10000 }); + cy.wait(aliasNames); cy.intercept('GET', 'internal/apm/suggestions?*').as( 'suggestionsRequest' @@ -241,11 +247,11 @@ describe('Service Overview', () => { it('when clicking the refresh button', () => { cy.contains('Refresh').click(); - cy.wait(aliasNames, { requestTimeout: 10000 }); + cy.wait(aliasNames); }); it.skip('when selecting a different time range and clicking the update button', () => { - cy.wait(aliasNames, { requestTimeout: 10000 }); + cy.wait(aliasNames); const timeStart = moment(start).subtract(5, 'm').toISOString(); const timeEnd = moment(end).subtract(5, 'm').toISOString(); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/time_comparison.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/time_comparison.spec.ts index 25591cd358bd5..611455163eca3 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/time_comparison.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/time_comparison.spec.ts @@ -51,8 +51,8 @@ const apisToIntercept = [ ]; describe.skip('Service overview: Time Comparison', () => { - before(async () => { - await synthtrace.index( + before(() => { + synthtrace.index( opbeans({ from: new Date(start).getTime(), to: new Date(end).getTime(), @@ -60,8 +60,8 @@ describe.skip('Service overview: Time Comparison', () => { ); }); - after(async () => { - await synthtrace.clean(); + after(() => { + synthtrace.clean(); }); beforeEach(() => { @@ -69,14 +69,14 @@ describe.skip('Service overview: Time Comparison', () => { }); it('enables by default the time comparison feature with Last 24 hours selected', () => { - cy.visit(serviceOverviewPath); + cy.visitKibana(serviceOverviewPath); cy.url().should('include', 'comparisonEnabled=true'); cy.url().should('include', 'offset=1d'); }); describe('when comparison is toggled off', () => { it('disables select box', () => { - cy.visit(serviceOverviewHref); + cy.visitKibana(serviceOverviewHref); cy.contains('opbeans-java'); // Comparison is enabled by default @@ -91,7 +91,7 @@ describe.skip('Service overview: Time Comparison', () => { apisToIntercept.map(({ endpoint, name }) => { cy.intercept('GET', endpoint).as(name); }); - cy.visit(serviceOverviewHref); + cy.visitKibana(serviceOverviewHref); cy.get('[data-test-subj="comparisonSelect"]').should('be.enabled'); const offset = `offset=1d`; @@ -125,7 +125,7 @@ describe.skip('Service overview: Time Comparison', () => { apisToIntercept.map(({ endpoint, name }) => { cy.intercept('GET', endpoint).as(name); }); - cy.visit(serviceOverviewPath); + cy.visitKibana(serviceOverviewPath); cy.contains('opbeans-java'); // opens the page with "Day before" selected cy.get('[data-test-subj="comparisonSelect"]').should('have.value', '1d'); @@ -136,7 +136,7 @@ describe.skip('Service overview: Time Comparison', () => { }); it('changes comparison type when a new time range is selected', () => { - cy.visit(serviceOverviewHref); + cy.visitKibana(serviceOverviewHref); cy.contains('opbeans-java'); // Time comparison default value cy.get('[data-test-subj="comparisonSelect"]').should('have.value', '1d'); @@ -189,7 +189,7 @@ describe.skip('Service overview: Time Comparison', () => { apisToIntercept.map(({ endpoint, name }) => { cy.intercept('GET', endpoint).as(name); }); - cy.visit( + cy.visitKibana( url.format({ pathname: serviceOverviewPath, query: { diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transaction_details/generate_span_links_data.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transaction_details/generate_span_links_data.ts index 0ced34d8a1b7c..9fd2cfe6eab0b 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transaction_details/generate_span_links_data.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transaction_details/generate_span_links_data.ts @@ -296,7 +296,7 @@ function getConsumerMultiple({ * ----Span E * ------span.links= producer-external-only / Span B | producer-consumer / Transaction C */ -export async function generateSpanLinksData() { +export function generateSpanLinksData() { const producerInternalOnly = getProducerInternalOnly(); const producerExternalOnly = getProducerExternalOnly(); const producerConsumer = getProducerConsumer({ @@ -309,7 +309,7 @@ export async function generateSpanLinksData() { producerExternalOnlySpanBSpanLink: producerExternalOnly.spanBSpanLink, }); - await synthtrace.index( + synthtrace.index( new EntityArrayIterable(producerInternalOnly.apmFields).merge( new EntityArrayIterable(producerExternalOnly.apmFields), new EntityArrayIterable(producerConsumer.apmFields), diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transaction_details/span_links.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transaction_details/span_links.spec.ts index 3dff8f075f187..cddba048e8a18 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transaction_details/span_links.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transaction_details/span_links.spec.ts @@ -35,17 +35,17 @@ describe('Span links', () => { describe('when data is loaded', () => { let ids: Awaited>; - before(async () => { - ids = await generateSpanLinksData(); + before(() => { + ids = generateSpanLinksData(); }); - after(async () => { - await synthtrace.clean(); + after(() => { + synthtrace.clean(); }); describe('span links count on trace waterfall', () => { it('Shows two children and no parents on producer-internal-only Span A', () => { - cy.visit( + cy.visitKibana( getServiceInventoryUrl({ serviceName: 'producer-internal-only' }) ); cy.contains('Transaction A').click(); @@ -59,7 +59,7 @@ describe('Span links', () => { }); it('Shows one parent and one children on producer-external-only Span B', () => { - cy.visit( + cy.visitKibana( getServiceInventoryUrl({ serviceName: 'producer-external-only' }) ); cy.contains('Transaction B').click(); @@ -73,7 +73,9 @@ describe('Span links', () => { }); it('Shows one parent and one children on producer-consumer Transaction C', () => { - cy.visit(getServiceInventoryUrl({ serviceName: 'producer-consumer' })); + cy.visitKibana( + getServiceInventoryUrl({ serviceName: 'producer-consumer' }) + ); cy.contains('Transaction C').click(); cy.contains('2 Span links'); cy.get( @@ -85,7 +87,9 @@ describe('Span links', () => { }); it('Shows no parent and one children on producer-consumer Span C', () => { - cy.visit(getServiceInventoryUrl({ serviceName: 'producer-consumer' })); + cy.visitKibana( + getServiceInventoryUrl({ serviceName: 'producer-consumer' }) + ); cy.contains('Transaction C').click(); cy.contains('1 Span link'); cy.get( @@ -97,7 +101,9 @@ describe('Span links', () => { }); it('Shows two parents and one children on consumer-multiple Transaction D', () => { - cy.visit(getServiceInventoryUrl({ serviceName: 'consumer-multiple' })); + cy.visitKibana( + getServiceInventoryUrl({ serviceName: 'consumer-multiple' }) + ); cy.contains('Transaction D').click(); cy.contains('2 Span links'); cy.get( @@ -109,7 +115,9 @@ describe('Span links', () => { }); it('Shows two parents and one children on consumer-multiple Span E', () => { - cy.visit(getServiceInventoryUrl({ serviceName: 'consumer-multiple' })); + cy.visitKibana( + getServiceInventoryUrl({ serviceName: 'consumer-multiple' }) + ); cy.contains('Transaction D').click(); cy.contains('2 Span links'); cy.get( @@ -123,7 +131,7 @@ describe('Span links', () => { describe('span link flyout', () => { it('Shows children details on producer-internal-only Span A', () => { - cy.visit( + cy.visitKibana( getServiceInventoryUrl({ serviceName: 'producer-internal-only' }) ); cy.contains('Transaction A').click(); @@ -154,7 +162,7 @@ describe('Span links', () => { }); it('Shows children and parents details on producer-external-only Span B', () => { - cy.visit( + cy.visitKibana( getServiceInventoryUrl({ serviceName: 'producer-external-only' }) ); cy.contains('Transaction B').click(); @@ -178,7 +186,9 @@ describe('Span links', () => { }); it('Shows children and parents details on producer-consumer Transaction C', () => { - cy.visit(getServiceInventoryUrl({ serviceName: 'producer-consumer' })); + cy.visitKibana( + getServiceInventoryUrl({ serviceName: 'producer-consumer' }) + ); cy.contains('Transaction C').click(); cy.get( `[aria-controls="${ids.producerConsumerIds.transactionCId}"]` @@ -210,7 +220,9 @@ describe('Span links', () => { }); it('Shows children and parents details on producer-consumer Span C', () => { - cy.visit(getServiceInventoryUrl({ serviceName: 'producer-consumer' })); + cy.visitKibana( + getServiceInventoryUrl({ serviceName: 'producer-consumer' }) + ); cy.contains('Transaction C').click(); cy.contains('Span C').click(); cy.get('[data-test-subj="spanLinksTab"]').click(); @@ -232,7 +244,9 @@ describe('Span links', () => { }); it('Shows children and parents details on consumer-multiple Transaction D', () => { - cy.visit(getServiceInventoryUrl({ serviceName: 'consumer-multiple' })); + cy.visitKibana( + getServiceInventoryUrl({ serviceName: 'consumer-multiple' }) + ); cy.contains('Transaction D').click(); cy.get( `[aria-controls="${ids.producerMultipleIds.transactionDId}"]` @@ -266,7 +280,9 @@ describe('Span links', () => { }); it('Shows children and parents details on consumer-multiple Span E', () => { - cy.visit(getServiceInventoryUrl({ serviceName: 'consumer-multiple' })); + cy.visitKibana( + getServiceInventoryUrl({ serviceName: 'consumer-multiple' }) + ); cy.contains('Transaction D').click(); cy.contains('Span E').click(); cy.get('[data-test-subj="spanLinksTab"]').click(); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transaction_details/transaction_details.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transaction_details/transaction_details.spec.ts index c5eeda6645ce8..ee2aade9ec1df 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transaction_details/transaction_details.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transaction_details/transaction_details.spec.ts @@ -17,8 +17,8 @@ const timeRange = { }; describe('Transaction details', () => { - before(async () => { - await synthtrace.index( + before(() => { + synthtrace.index( opbeans({ from: new Date(start).getTime(), to: new Date(end).getTime(), @@ -26,13 +26,13 @@ describe('Transaction details', () => { ); }); - after(async () => { - await synthtrace.clean(); + after(() => { + synthtrace.clean(); }); beforeEach(() => { cy.loginAsViewerUser(); - cy.visit( + cy.visitKibana( `/app/apm/services/opbeans-java/transactions/view?${new URLSearchParams({ ...timeRange, transactionName: 'GET /api/product', diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transactions_overview/transactions_overview.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transactions_overview/transactions_overview.spec.ts index 671b35741f91b..83753b7fe2595 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transactions_overview/transactions_overview.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/transactions_overview/transactions_overview.spec.ts @@ -19,8 +19,8 @@ const serviceTransactionsHref = url.format({ }); describe('Transactions Overview', () => { - before(async () => { - await synthtrace.index( + before(() => { + synthtrace.index( opbeans({ from: new Date(start).getTime(), to: new Date(end).getTime(), @@ -28,8 +28,8 @@ describe('Transactions Overview', () => { ); }); - after(async () => { - await synthtrace.clean(); + after(() => { + synthtrace.clean(); }); beforeEach(() => { @@ -37,7 +37,7 @@ describe('Transactions Overview', () => { }); it('has no detectable a11y violations on load', () => { - cy.visit(serviceTransactionsHref); + cy.visitKibana(serviceTransactionsHref); cy.get('a:contains(Transactions)').should( 'have.attr', 'aria-selected', @@ -48,7 +48,7 @@ describe('Transactions Overview', () => { }); it('persists transaction type selected when navigating to Overview tab', () => { - cy.visit(serviceTransactionsHref); + cy.visitKibana(serviceTransactionsHref); cy.get('[data-test-subj="headerFilterTransactionType"]').should( 'have.value', 'request' diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/tutorial/tutorial.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/tutorial/tutorial.spec.ts index e2ebe9c2bdda8..cb8d9218b4ec1 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/tutorial/tutorial.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/tutorial/tutorial.spec.ts @@ -6,9 +6,9 @@ */ describe('APM tutorial', () => { - before(() => { + beforeEach(() => { cy.loginAsViewerUser(); - cy.visit('/app/home#/tutorial/apm'); + cy.visitKibana('/app/home#/tutorial/apm'); }); it('includes section for APM Server', () => { diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts b/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts index 0559b07b1cbf2..37182e328ebf3 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts @@ -21,21 +21,24 @@ Cypress.Commands.add('loginAsEditorUser', () => { Cypress.Commands.add( 'loginAs', ({ username, password }: { username: string; password: string }) => { - cy.log(`Logging in as ${username}`); - const kibanaUrl = Cypress.env('KIBANA_URL'); - return cy.request({ - log: false, - method: 'POST', - url: `${kibanaUrl}/internal/security/login`, - body: { - providerType: 'basic', - providerName: 'basic', - currentURL: `${kibanaUrl}/login`, - params: { username, password }, - }, - headers: { - 'kbn-xsrf': 'e2e_test', - }, + cy.log(`Calling 'loginAs'`); + cy.session([username, password], () => { + cy.log(`Logging in as ${username}`); + const kibanaUrl = Cypress.env('KIBANA_URL'); + cy.request({ + log: false, + method: 'POST', + url: `${kibanaUrl}/internal/security/login`, + body: { + providerType: 'basic', + providerName: 'basic', + currentURL: `${kibanaUrl}/login`, + params: { username, password }, + }, + headers: { + 'kbn-xsrf': 'e2e_test', + }, + }); }); } ); @@ -45,6 +48,14 @@ Cypress.Commands.add('changeTimeRange', (value: string) => { cy.contains(value).click(); }); +Cypress.Commands.add('visitKibana', (url: string) => { + cy.visit(url); + cy.get('[data-test-subj="kbnLoadingMessage"]').should('exist'); + cy.get('[data-test-subj="kbnLoadingMessage"]').should('not.exist', { + timeout: 50000, + }); +}); + Cypress.Commands.add( 'selectAbsoluteTimeRange', (start: string, end: string) => { @@ -96,6 +107,7 @@ Cypress.Commands.add( headers: { 'kbn-xsrf': 'e2e_test', }, + auth: { user: 'editor', pass: 'changeme' }, }); } ); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/support/types.d.ts b/x-pack/plugins/apm/ftr_e2e/cypress/support/types.d.ts index f46a54142de0e..27720210b668a 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/support/types.d.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/support/types.d.ts @@ -14,6 +14,7 @@ declare namespace Cypress { password: string; }): Cypress.Chainable>; changeTimeRange(value: string): void; + visitKibana(url: string): void; selectAbsoluteTimeRange(start: string, end: string): void; expectAPIsToHaveBeenCalledWith(params: { apisIntercepted: string[]; diff --git a/x-pack/plugins/apm/ftr_e2e/synthtrace.ts b/x-pack/plugins/apm/ftr_e2e/synthtrace.ts index 6fb880c40b0cc..a421edea04a6e 100644 --- a/x-pack/plugins/apm/ftr_e2e/synthtrace.ts +++ b/x-pack/plugins/apm/ftr_e2e/synthtrace.ts @@ -8,11 +8,6 @@ import type { EntityIterable } from '@kbn/apm-synthtrace'; export const synthtrace = { index: (events: EntityIterable) => - new Promise((resolve) => { - cy.task('synthtrace:index', events.toArray()).then(resolve); - }), - clean: () => - new Promise((resolve) => { - cy.task('synthtrace:clean').then(resolve); - }), + cy.task('synthtrace:index', events.toArray()), + clean: () => cy.task('synthtrace:clean'), }; diff --git a/x-pack/plugins/apm/public/application/application.test.tsx b/x-pack/plugins/apm/public/application/application.test.tsx index 62adf0b2cb355..3c2d46ef3a107 100644 --- a/x-pack/plugins/apm/public/application/application.test.tsx +++ b/x-pack/plugins/apm/public/application/application.test.tsx @@ -122,7 +122,8 @@ describe('renderApp (APM)', () => { }; }; - it('renders the app', () => { + it('renders the app', async () => { + const promise = Promise.resolve(); const mountProps = getApmMountProps(); let unmount: () => void; @@ -131,6 +132,11 @@ describe('renderApp (APM)', () => { unmount = renderApmApp(mountProps); }); + // fake promise to wait for to ensure the app is mounted + await act(async () => { + await promise; + }); + expect(() => { unmount(); }).not.toThrowError(); diff --git a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/index.test.tsx b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/index.test.tsx index 763d4cf0717a1..c8ab3acd1a0af 100644 --- a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/index.test.tsx +++ b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/index.test.tsx @@ -4,7 +4,11 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { isMetricsTabHidden, isMetricsJVMsTabHidden } from '.'; +import { + isMetricsTabHidden, + isMetricsJVMsTabHidden, + isInfraTabHidden, +} from '.'; describe('APM service template', () => { describe('isMetricsTabHidden', () => { @@ -71,4 +75,38 @@ describe('APM service template', () => { }); }); }); + describe('isInfraTabHidden', () => { + describe('hides infra tab', () => { + [ + { agentName: undefined }, + { agentName: 'js-base' }, + { agentName: 'rum-js' }, + { agentName: 'opentelemetry/webjs' }, + { agentName: 'ios/swift' }, + { runtimeName: 'aws_lambda' }, + ].map((input) => { + it(`when input ${JSON.stringify(input)}`, () => { + expect(isInfraTabHidden(input)).toBeTruthy(); + }); + }); + }); + describe('shows infra tab', () => { + [ + { agentName: 'ruby', runtimeName: 'ruby' }, + { agentName: 'ruby', runtimeName: 'jruby' }, + { agentName: 'ruby' }, + { agentName: 'dotnet' }, + { agentName: 'go' }, + { agentName: 'nodejs' }, + { agentName: 'php' }, + { agentName: 'python' }, + { agentName: 'java' }, + { agentName: 'opentelemetry/java' }, + ].map((input) => { + it(`when input ${JSON.stringify(input)}`, () => { + expect(isInfraTabHidden(input)).toBeFalsy(); + }); + }); + }); + }); }); diff --git a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/index.tsx b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/index.tsx index c6c4c405d11af..2abfdba6f4124 100644 --- a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/index.tsx +++ b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/index.tsx @@ -167,6 +167,21 @@ export function isMetricsJVMsTabHidden({ ); } +export function isInfraTabHidden({ + agentName, + runtimeName, +}: { + agentName?: string; + runtimeName?: string; +}) { + return ( + !agentName || + isRumAgentName(agentName) || + isMobileAgentName(agentName) || + isServerlessAgent(runtimeName) + ); +} + function useTabs({ selectedTab }: { selectedTab: Tab['key'] }) { const { agentName, runtimeName } = useApmServiceContext(); const { config, core, plugins } = useApmPluginContext(); @@ -266,6 +281,7 @@ function useTabs({ selectedTab }: { selectedTab: Tab['key'] }) { label: i18n.translate('xpack.apm.home.infraTabLabel', { defaultMessage: 'Infrastructure', }), + hidden: isInfraTabHidden({ agentName, runtimeName }), }, { key: 'service-map', diff --git a/x-pack/plugins/canvas/public/components/workpad_app/workpad_app.component.tsx b/x-pack/plugins/canvas/public/components/workpad_app/workpad_app.component.tsx index d7eafc93ff1a2..5301c00f1f82b 100644 --- a/x-pack/plugins/canvas/public/components/workpad_app/workpad_app.component.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_app/workpad_app.component.tsx @@ -67,7 +67,6 @@ export const WorkpadApp: FC = ({ deselectElement, isWriteable, workpad })
{/* NOTE: canvasWorkpadContainer is used for exporting */}
({ - deselectElement: (ev) => { - ev.stopPropagation(); - dispatch(selectToplevelNodes([])); - }, -}); - -export const WorkpadApp = connect( - (state: State) => ({ - isWriteable: isWriteable(state) && canUserWrite(state), - workpad: getWorkpad(state), - }), - mapDispatchToProps -)(WorkpadAppComponent); +export const WorkpadApp = connect((state: State) => ({ + isWriteable: isWriteable(state) && canUserWrite(state), + workpad: getWorkpad(state), +}))(WorkpadAppComponent); diff --git a/x-pack/plugins/canvas/public/components/workpad_color_picker/workpad_color_picker.component.tsx b/x-pack/plugins/canvas/public/components/workpad_color_picker/workpad_color_picker.component.tsx index 0561ac005519b..c14e54d8e8549 100644 --- a/x-pack/plugins/canvas/public/components/workpad_color_picker/workpad_color_picker.component.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_color_picker/workpad_color_picker.component.tsx @@ -5,8 +5,11 @@ * 2.0. */ -import React from 'react'; +import React, { useCallback } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; import { i18n } from '@kbn/i18n'; +import { addColor, removeColor } from '../../state/actions/workpad'; +import { getWorkpadColors } from '../../state/selectors/workpad'; import { ColorPickerPopover, Props } from '../color_picker_popover'; const strings = { @@ -17,9 +20,17 @@ const strings = { }; export const WorkpadColorPicker = (props: Props) => { + const dispatch = useDispatch(); + const onAddColor = useCallback((payload) => dispatch(addColor(payload)), [dispatch]); + const onRemoveColor = useCallback((payload) => dispatch(removeColor(payload)), [dispatch]); + const colors = useSelector(getWorkpadColors); + return ( diff --git a/x-pack/plugins/canvas/public/components/workpad_color_picker/workpad_color_picker.ts b/x-pack/plugins/canvas/public/components/workpad_color_picker/workpad_color_picker.ts index 140de2b73ddf0..fe029c88c8cf1 100644 --- a/x-pack/plugins/canvas/public/components/workpad_color_picker/workpad_color_picker.ts +++ b/x-pack/plugins/canvas/public/components/workpad_color_picker/workpad_color_picker.ts @@ -5,20 +5,4 @@ * 2.0. */ -import { connect } from 'react-redux'; -import { addColor, removeColor } from '../../state/actions/workpad'; -import { getWorkpadColors } from '../../state/selectors/workpad'; - -import { WorkpadColorPicker as Component } from './workpad_color_picker.component'; -import { State } from '../../../types'; - -const mapStateToProps = (state: State) => ({ - colors: getWorkpadColors(state), -}); - -const mapDispatchToProps = { - onAddColor: addColor, - onRemoveColor: removeColor, -}; - -export const WorkpadColorPicker = connect(mapStateToProps, mapDispatchToProps)(Component); +export { WorkpadColorPicker } from './workpad_color_picker.component'; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/refresh_control/refresh_control.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/refresh_control/refresh_control.component.tsx index 7b1df158087b4..ce93c370aa0f6 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/refresh_control/refresh_control.component.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/refresh_control/refresh_control.component.tsx @@ -5,11 +5,15 @@ * 2.0. */ -import React, { MouseEventHandler } from 'react'; +import React, { useCallback } from 'react'; import PropTypes from 'prop-types'; import { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { useDispatch, useSelector } from 'react-redux'; +// @ts-expect-error untyped local +import { fetchAllRenderables } from '../../../state/actions/elements'; +import { getInFlight } from '../../../state/selectors/resolved_args'; import { ToolTipShortcut } from '../../tool_tip_shortcut'; const strings = { @@ -23,30 +27,31 @@ const strings = { }), }; -export interface Props { - doRefresh: MouseEventHandler; - inFlight: boolean; -} +export const RefreshControl = () => { + const dispatch = useDispatch(); + const inFlight = useSelector(getInFlight); + const doRefresh = useCallback(() => dispatch(fetchAllRenderables()), [dispatch]); -export const RefreshControl = ({ doRefresh, inFlight }: Props) => ( - - {strings.getRefreshTooltip()} - - - } - > - - -); + return ( + + {strings.getRefreshTooltip()} + + + } + > + + + ); +}; RefreshControl.propTypes = { doRefresh: PropTypes.func.isRequired, diff --git a/x-pack/plugins/canvas/public/components/workpad_header/refresh_control/refresh_control.ts b/x-pack/plugins/canvas/public/components/workpad_header/refresh_control/refresh_control.ts index c9182eea7ff4d..ef620ee897dc3 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/refresh_control/refresh_control.ts +++ b/x-pack/plugins/canvas/public/components/workpad_header/refresh_control/refresh_control.ts @@ -5,19 +5,4 @@ * 2.0. */ -import { connect } from 'react-redux'; -// @ts-expect-error untyped local -import { fetchAllRenderables } from '../../../state/actions/elements'; -import { getInFlight } from '../../../state/selectors/resolved_args'; -import { State } from '../../../../types'; -import { RefreshControl as Component } from './refresh_control.component'; - -const mapStateToProps = (state: State) => ({ - inFlight: getInFlight(state), -}); - -const mapDispatchToProps = { - doRefresh: fetchAllRenderables, -}; - -export const RefreshControl = connect(mapStateToProps, mapDispatchToProps)(Component); +export { RefreshControl } from './refresh_control.component'; diff --git a/x-pack/plugins/canvas/public/components/workpad_page/workpad_interactive_page/event_handlers.ts b/x-pack/plugins/canvas/public/components/workpad_page/workpad_interactive_page/event_handlers.ts index e8527089c9ac4..bb06444c0c8f6 100644 --- a/x-pack/plugins/canvas/public/components/workpad_page/workpad_interactive_page/event_handlers.ts +++ b/x-pack/plugins/canvas/public/components/workpad_page/workpad_interactive_page/event_handlers.ts @@ -76,7 +76,6 @@ const setupHandler = (commit: CommitFn, canvasOrigin: CanvasOriginFn, zoomScale? return; } - e.stopPropagation(); const { x, y } = localMousePosition(canvasOrigin, clientX, clientY, zoomScale); commit('mouseEvent', { event: 'mouseUp', x, y, altKey, metaKey, shiftKey, ctrlKey }); resetHandler(); @@ -123,7 +122,6 @@ const handleMouseDown = ( return; } - e.stopPropagation(); if (buttons !== 1 || !commit) { resetHandler(); return; // left-click only diff --git a/x-pack/plugins/cases/public/common/mock/test_providers.tsx b/x-pack/plugins/cases/public/common/mock/test_providers.tsx index 46dda67c23f7f..dceb8fd0f30a7 100644 --- a/x-pack/plugins/cases/public/common/mock/test_providers.tsx +++ b/x-pack/plugins/cases/public/common/mock/test_providers.tsx @@ -12,7 +12,7 @@ import { ThemeProvider } from 'styled-components'; import { render as reactRender, RenderOptions, RenderResult } from '@testing-library/react'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import { QueryClient, QueryClientProvider } from 'react-query'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { SECURITY_SOLUTION_OWNER } from '../../../common/constants'; import { CasesCapabilities, CasesFeatures, CasesPermissions } from '../../../common/ui/types'; import { CasesProvider } from '../../components/cases_context'; diff --git a/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.test.tsx b/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.test.tsx index eba8888f3367a..98c89215aac21 100644 --- a/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.test.tsx @@ -12,7 +12,7 @@ import { AllCasesSelectorModal } from '.'; import { TestProviders } from '../../../common/mock'; import { AllCasesList } from '../all_cases_list'; -jest.mock('../all_cases_list'); +jest.mock('../all_cases_list', () => ({ AllCasesList: jest.fn().mockReturnValue(<>) })); const onRowClick = jest.fn(); const defaultProps = { @@ -57,8 +57,7 @@ describe('AllCasesSelectorModal', () => { ); - // @ts-ignore idk what this mock style is but it works ¯\_(ツ)_/¯ - expect(AllCasesList.type.mock.calls[0][0]).toEqual( + expect((AllCasesList as unknown as jest.Mock).mock.calls[0][0]).toEqual( expect.objectContaining({ hiddenStatuses: fullProps.hiddenStatuses, isSelectorView: true, diff --git a/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.tsx b/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.tsx index 173d4ec76b230..fccdf76abd2c4 100644 --- a/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/selector_modal/all_cases_selector_modal.tsx @@ -15,7 +15,7 @@ import { EuiModalHeaderTitle, } from '@elastic/eui'; import styled from 'styled-components'; -import { QueryClientProvider } from 'react-query'; +import { QueryClientProvider } from '@tanstack/react-query'; import { Case, CaseStatusWithAllStatus } from '../../../../common/ui/types'; import * as i18n from '../../../common/translations'; import { AllCasesList } from '../all_cases_list'; diff --git a/x-pack/plugins/cases/public/components/app/routes.tsx b/x-pack/plugins/cases/public/components/app/routes.tsx index 6e952ce84ba1e..062007f3341e6 100644 --- a/x-pack/plugins/cases/public/components/app/routes.tsx +++ b/x-pack/plugins/cases/public/components/app/routes.tsx @@ -8,7 +8,7 @@ import React, { lazy, Suspense, useCallback } from 'react'; import { Redirect, Switch } from 'react-router-dom'; import { Route } from '@kbn/kibana-react-plugin/public'; -import { QueryClientProvider } from 'react-query'; +import { QueryClientProvider } from '@tanstack/react-query'; import { EuiLoadingSpinner } from '@elastic/eui'; import { AllCases } from '../all_cases'; import { CreateCase } from '../create'; diff --git a/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx b/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx index ea9ce12828938..284785b33d720 100644 --- a/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx @@ -40,7 +40,9 @@ jest.mock('../../containers/use_get_tags'); jest.mock('../../containers/use_get_case'); jest.mock('../../containers/configure/use_connectors'); jest.mock('../../containers/use_post_push_to_service'); -jest.mock('../user_actions/timestamp'); +jest.mock('../user_actions/timestamp', () => ({ + UserActionTimestamp: () => <>, +})); jest.mock('../../common/navigation/hooks'); jest.mock('../../common/hooks'); jest.mock('../connectors/resilient/api'); diff --git a/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.test.tsx b/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.test.tsx index 8a3a457bcfd00..db5411525290b 100644 --- a/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.test.tsx @@ -30,7 +30,9 @@ import { useGetTags } from '../../../containers/use_get_tags'; jest.mock('../../../containers/use_get_case_user_actions'); jest.mock('../../../containers/configure/use_connectors'); jest.mock('../../../containers/use_post_push_to_service'); -jest.mock('../../user_actions/timestamp'); +jest.mock('../../user_actions/timestamp', () => ({ + UserActionTimestamp: () => <>, +})); jest.mock('../../../common/navigation/hooks'); jest.mock('../../../containers/use_get_action_license'); jest.mock('../../../containers/use_get_tags'); diff --git a/x-pack/plugins/cases/public/components/case_view/index.test.tsx b/x-pack/plugins/cases/public/components/case_view/index.test.tsx index b9213a8eb887f..20cf2c8d0d9b4 100644 --- a/x-pack/plugins/cases/public/components/case_view/index.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/index.test.tsx @@ -49,7 +49,9 @@ jest.mock('../../containers/use_get_case'); jest.mock('../../containers/use_get_case_metrics'); jest.mock('../../containers/configure/use_connectors'); jest.mock('../../containers/use_post_push_to_service'); -jest.mock('../user_actions/timestamp'); +jest.mock('../user_actions/timestamp', () => ({ + UserActionTimestamp: () => <>, +})); jest.mock('../../common/lib/kibana'); jest.mock('../../common/navigation/hooks'); jest.mock('../../containers/api'); @@ -171,7 +173,7 @@ describe('CaseView', () => { const queryClientSpy = jest.spyOn(appMockRenderer.queryClient, 'invalidateQueries'); const result = appMockRenderer.render(); userEvent.click(result.getByTestId('case-refresh')); - expect(queryClientSpy).toHaveBeenCalledWith('case'); + expect(queryClientSpy).toHaveBeenCalledWith(['case']); }); describe('when a `refreshRef` prop is provided', () => { @@ -203,7 +205,7 @@ describe('CaseView', () => { it('should refresh actions and comments', async () => { refreshRef!.current!.refreshCase(); await waitFor(() => { - expect(queryClientSpy).toHaveBeenCalledWith(CASE_VIEW_CACHE_KEY); + expect(queryClientSpy).toHaveBeenCalledWith([CASE_VIEW_CACHE_KEY]); }); }); }); diff --git a/x-pack/plugins/cases/public/components/case_view/use_on_refresh_case_view_page.tsx b/x-pack/plugins/cases/public/components/case_view/use_on_refresh_case_view_page.tsx index a4c36deabef80..396f180a074a0 100644 --- a/x-pack/plugins/cases/public/components/case_view/use_on_refresh_case_view_page.tsx +++ b/x-pack/plugins/cases/public/components/case_view/use_on_refresh_case_view_page.tsx @@ -6,7 +6,7 @@ */ import { useCallback } from 'react'; -import { useQueryClient } from 'react-query'; +import { useQueryClient } from '@tanstack/react-query'; import { CASE_TAGS_CACHE_KEY, CASE_VIEW_CACHE_KEY } from '../../containers/constants'; /** @@ -20,7 +20,7 @@ import { CASE_TAGS_CACHE_KEY, CASE_VIEW_CACHE_KEY } from '../../containers/const export const useRefreshCaseViewPage = () => { const queryClient = useQueryClient(); return useCallback(() => { - queryClient.invalidateQueries(CASE_VIEW_CACHE_KEY); - queryClient.invalidateQueries(CASE_TAGS_CACHE_KEY); + queryClient.invalidateQueries([CASE_VIEW_CACHE_KEY]); + queryClient.invalidateQueries([CASE_TAGS_CACHE_KEY]); }, [queryClient]); }; diff --git a/x-pack/plugins/cases/public/components/cases_context/query_client.ts b/x-pack/plugins/cases/public/components/cases_context/query_client.ts index 08a135f5f9395..3f27e7b550adf 100644 --- a/x-pack/plugins/cases/public/components/cases_context/query_client.ts +++ b/x-pack/plugins/cases/public/components/cases_context/query_client.ts @@ -5,6 +5,6 @@ * 2.0. */ -import { QueryClient } from 'react-query'; +import { QueryClient } from '@tanstack/react-query'; export const casesQueryClient = new QueryClient(); diff --git a/x-pack/plugins/cases/public/components/create/flyout/create_case_flyout.tsx b/x-pack/plugins/cases/public/components/create/flyout/create_case_flyout.tsx index be758e1718451..c7bcb9b0828c6 100644 --- a/x-pack/plugins/cases/public/components/create/flyout/create_case_flyout.tsx +++ b/x-pack/plugins/cases/public/components/create/flyout/create_case_flyout.tsx @@ -9,7 +9,7 @@ import React from 'react'; import styled, { createGlobalStyle } from 'styled-components'; import { EuiFlyout, EuiFlyoutHeader, EuiTitle, EuiFlyoutBody } from '@elastic/eui'; -import { QueryClientProvider } from 'react-query'; +import { QueryClientProvider } from '@tanstack/react-query'; import * as i18n from '../translations'; import { Case } from '../../../../common/ui/types'; import { CreateCaseForm } from '../form'; diff --git a/x-pack/plugins/cases/public/components/header_page/__snapshots__/editable_title.test.tsx.snap b/x-pack/plugins/cases/public/components/header_page/__snapshots__/editable_title.test.tsx.snap index bc4dba5843e62..73f466aeec771 100644 --- a/x-pack/plugins/cases/public/components/header_page/__snapshots__/editable_title.test.tsx.snap +++ b/x-pack/plugins/cases/public/components/header_page/__snapshots__/editable_title.test.tsx.snap @@ -14,11 +14,38 @@ exports[`EditableTitle renders 1`] = ` "retry": false, }, }, + "logger": BufferedConsole { + "Console": [Function], + "_buffer": Array [], + "_counters": Object {}, + "_groupDepth": 0, + "_timers": Object {}, + "assert": [Function], + "clear": [Function], + "count": [Function], + "countReset": [Function], + "debug": [Function], + "dir": [Function], + "dirxml": [Function], + "error": [Function], + "group": [Function], + "groupCollapsed": [Function], + "groupEnd": [Function], + "info": [Function], + "log": [Function], + "table": [Function], + "time": [Function], + "timeEnd": [Function], + "timeLog": [Function], + "trace": [Function], + "warn": [Function], + }, "mutationCache": MutationCache { "config": Object {}, "listeners": Array [], "mutationId": 0, "mutations": Array [], + "subscribe": [Function], }, "mutationDefaults": Array [], "queryCache": QueryCache { @@ -26,6 +53,7 @@ exports[`EditableTitle renders 1`] = ` "listeners": Array [], "queries": Array [], "queriesMap": Object {}, + "subscribe": [Function], }, "queryDefaults": Array [], } diff --git a/x-pack/plugins/cases/public/components/header_page/__snapshots__/index.test.tsx.snap b/x-pack/plugins/cases/public/components/header_page/__snapshots__/index.test.tsx.snap index 083975a40330e..7e6d9e2b05d94 100644 --- a/x-pack/plugins/cases/public/components/header_page/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/cases/public/components/header_page/__snapshots__/index.test.tsx.snap @@ -14,11 +14,38 @@ exports[`HeaderPage it renders 1`] = ` "retry": false, }, }, + "logger": BufferedConsole { + "Console": [Function], + "_buffer": Array [], + "_counters": Object {}, + "_groupDepth": 0, + "_timers": Object {}, + "assert": [Function], + "clear": [Function], + "count": [Function], + "countReset": [Function], + "debug": [Function], + "dir": [Function], + "dirxml": [Function], + "error": [Function], + "group": [Function], + "groupCollapsed": [Function], + "groupEnd": [Function], + "info": [Function], + "log": [Function], + "table": [Function], + "time": [Function], + "timeEnd": [Function], + "timeLog": [Function], + "trace": [Function], + "warn": [Function], + }, "mutationCache": MutationCache { "config": Object {}, "listeners": Array [], "mutationId": 0, "mutations": Array [], + "subscribe": [Function], }, "mutationDefaults": Array [], "queryCache": QueryCache { @@ -26,6 +53,7 @@ exports[`HeaderPage it renders 1`] = ` "listeners": Array [], "queries": Array [], "queriesMap": Object {}, + "subscribe": [Function], }, "queryDefaults": Array [], } diff --git a/x-pack/plugins/cases/public/components/recent_cases/index.tsx b/x-pack/plugins/cases/public/components/recent_cases/index.tsx index 4cc070e77b76a..c8b7581e53107 100644 --- a/x-pack/plugins/cases/public/components/recent_cases/index.tsx +++ b/x-pack/plugins/cases/public/components/recent_cases/index.tsx @@ -8,7 +8,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiText, EuiTitle } from '@elastic/eui'; import React, { useCallback, useMemo, useState } from 'react'; -import { QueryClientProvider } from 'react-query'; +import { QueryClientProvider } from '@tanstack/react-query'; import * as i18n from './translations'; import { LinkAnchor } from '../links'; import { RecentCasesFilters } from './filters'; diff --git a/x-pack/plugins/cases/public/components/user_actions/index.test.tsx b/x-pack/plugins/cases/public/components/user_actions/index.test.tsx index 9a971552f5ec3..60fc0e92d024b 100644 --- a/x-pack/plugins/cases/public/components/user_actions/index.test.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/index.test.tsx @@ -53,7 +53,9 @@ const defaultProps = { }; jest.mock('../../containers/use_update_comment'); -jest.mock('./timestamp'); +jest.mock('./timestamp', () => ({ + UserActionTimestamp: () => <>, +})); jest.mock('../../common/lib/kibana'); const useUpdateCommentMock = useUpdateComment as jest.Mock; diff --git a/x-pack/plugins/cases/public/containers/configure/use_action_types.tsx b/x-pack/plugins/cases/public/containers/configure/use_action_types.tsx index caca1de7afcec..76a48e5d09ca3 100644 --- a/x-pack/plugins/cases/public/containers/configure/use_action_types.tsx +++ b/x-pack/plugins/cases/public/containers/configure/use_action_types.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import * as i18n from '../translations'; import { fetchActionTypes } from './api'; import { useToasts } from '../../common/lib/kibana'; diff --git a/x-pack/plugins/cases/public/containers/configure/use_connectors.tsx b/x-pack/plugins/cases/public/containers/configure/use_connectors.tsx index 9f1d3f38655ae..95124af988fb6 100644 --- a/x-pack/plugins/cases/public/containers/configure/use_connectors.tsx +++ b/x-pack/plugins/cases/public/containers/configure/use_connectors.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { fetchConnectors } from './api'; import { useApplicationCapabilities, useToasts } from '../../common/lib/kibana'; import * as i18n from './translations'; diff --git a/x-pack/plugins/cases/public/containers/use_get_action_license.tsx b/x-pack/plugins/cases/public/containers/use_get_action_license.tsx index a64a449783ba9..8e9aa28de440a 100644 --- a/x-pack/plugins/cases/public/containers/use_get_action_license.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_action_license.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { useToasts } from '../common/lib/kibana'; import { getActionLicense } from './api'; import * as i18n from './translations'; diff --git a/x-pack/plugins/cases/public/containers/use_get_case.test.tsx b/x-pack/plugins/cases/public/containers/use_get_case.test.tsx index 8a3d7263b78de..7b07d44bdda78 100644 --- a/x-pack/plugins/cases/public/containers/use_get_case.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_case.test.tsx @@ -10,7 +10,7 @@ import { useGetCase } from './use_get_case'; import * as api from './api'; import { waitFor } from '@testing-library/dom'; import React from 'react'; -import { QueryClient, QueryClientProvider } from 'react-query'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { useToasts } from '../common/lib/kibana'; jest.mock('./api'); diff --git a/x-pack/plugins/cases/public/containers/use_get_case.tsx b/x-pack/plugins/cases/public/containers/use_get_case.tsx index ff0cce344b87f..ded91240239a1 100644 --- a/x-pack/plugins/cases/public/containers/use_get_case.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_case.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { ResolvedCase } from './types'; import * as i18n from './translations'; import { useToasts } from '../common/lib/kibana'; diff --git a/x-pack/plugins/cases/public/containers/use_get_case_metrics.test.tsx b/x-pack/plugins/cases/public/containers/use_get_case_metrics.test.tsx index 3cd3904a24072..7f61397728170 100644 --- a/x-pack/plugins/cases/public/containers/use_get_case_metrics.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_case_metrics.test.tsx @@ -48,13 +48,11 @@ describe('useGetCaseMetrics', () => { throw new Error('Something went wrong'); }); - await act(async () => { - const { waitForNextUpdate } = renderHook(() => useGetCaseMetrics(basicCase.id, features), { - wrapper, - }); - await waitForNextUpdate(); - expect(spyOnGetCaseMetrics).toBeCalledWith(basicCase.id, features, abortCtrl.signal); - expect(addError).toHaveBeenCalled(); + const { waitForNextUpdate } = renderHook(() => useGetCaseMetrics(basicCase.id, features), { + wrapper, }); + await waitForNextUpdate(); + expect(spyOnGetCaseMetrics).toBeCalledWith(basicCase.id, features, abortCtrl.signal); + expect(addError).toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/cases/public/containers/use_get_case_metrics.tsx b/x-pack/plugins/cases/public/containers/use_get_case_metrics.tsx index 359bd6b7eeae9..1e294c4a5ba6e 100644 --- a/x-pack/plugins/cases/public/containers/use_get_case_metrics.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_case_metrics.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { SingleCaseMetrics, SingleCaseMetricsFeature } from './types'; import { useToasts } from '../common/lib/kibana'; import { getSingleCaseMetrics } from './api'; diff --git a/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx b/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx index 3d231a9660ba2..c5dbf017da8c9 100644 --- a/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_case_user_actions.test.tsx @@ -22,7 +22,7 @@ import { } from './mock'; import { Actions } from '../../common/api'; import React from 'react'; -import { QueryClientProvider } from 'react-query'; +import { QueryClientProvider } from '@tanstack/react-query'; import { testQueryClient } from '../common/mock'; import { waitFor } from '@testing-library/dom'; import * as api from './api'; diff --git a/x-pack/plugins/cases/public/containers/use_get_case_user_actions.tsx b/x-pack/plugins/cases/public/containers/use_get_case_user_actions.tsx index 9376598b1fdf4..da695201d6d76 100644 --- a/x-pack/plugins/cases/public/containers/use_get_case_user_actions.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_case_user_actions.tsx @@ -8,7 +8,7 @@ import { isEmpty, uniqBy } from 'lodash/fp'; import deepEqual from 'fast-deep-equal'; -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { CaseUserActions, CaseExternalService } from '../../common/ui/types'; import { ActionTypes, CaseConnector, NONE_CONNECTOR_ID } from '../../common/api'; import { getCaseUserActions } from './api'; diff --git a/x-pack/plugins/cases/public/containers/use_get_cases.tsx b/x-pack/plugins/cases/public/containers/use_get_cases.tsx index 6274ac863200f..ce19e68fa1798 100644 --- a/x-pack/plugins/cases/public/containers/use_get_cases.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_cases.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery, UseQueryResult } from 'react-query'; +import { useQuery, UseQueryResult } from '@tanstack/react-query'; import { CASE_LIST_CACHE_KEY, DEFAULT_TABLE_ACTIVE_PAGE, DEFAULT_TABLE_LIMIT } from './constants'; import { Cases, FilterOptions, QueryParams, SortFieldCase, StatusAll, SeverityAll } from './types'; import { useToasts } from '../common/lib/kibana'; diff --git a/x-pack/plugins/cases/public/containers/use_get_tags.tsx b/x-pack/plugins/cases/public/containers/use_get_tags.tsx index 3e18ac0a01315..1696a9d1413a5 100644 --- a/x-pack/plugins/cases/public/containers/use_get_tags.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_tags.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { useToasts } from '../common/lib/kibana'; import { useCasesContext } from '../components/cases_context/use_cases_context'; import { ServerError } from '../types'; diff --git a/x-pack/plugins/cases/public/containers/user_profiles/use_bulk_get_user_profiles.ts b/x-pack/plugins/cases/public/containers/user_profiles/use_bulk_get_user_profiles.ts index cdaee1848613c..78c310462f77e 100644 --- a/x-pack/plugins/cases/public/containers/user_profiles/use_bulk_get_user_profiles.ts +++ b/x-pack/plugins/cases/public/containers/user_profiles/use_bulk_get_user_profiles.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery, UseQueryResult } from 'react-query'; +import { useQuery, UseQueryResult } from '@tanstack/react-query'; import { UserProfile } from '@kbn/security-plugin/common'; import * as i18n from '../translations'; import { useKibana, useToasts } from '../../common/lib/kibana'; diff --git a/x-pack/plugins/cases/public/containers/user_profiles/use_suggest_user_profiles.test.ts b/x-pack/plugins/cases/public/containers/user_profiles/use_suggest_user_profiles.test.ts index 5ae16c8fa6e00..ef5fe32a23dff 100644 --- a/x-pack/plugins/cases/public/containers/user_profiles/use_suggest_user_profiles.test.ts +++ b/x-pack/plugins/cases/public/containers/user_profiles/use_suggest_user_profiles.test.ts @@ -26,21 +26,8 @@ describe('useSuggestUserProfiles', () => { let appMockRender: AppMockRenderer; - beforeAll(() => { - jest.useFakeTimers(); - }); - beforeEach(() => { appMockRender = createAppMockRenderer(); - jest.clearAllMocks(); - }); - - afterEach(() => { - jest.clearAllTimers(); - }); - - afterAll(() => { - jest.useRealTimers(); }); it('calls suggestUserProfiles with correct arguments', async () => { @@ -50,7 +37,6 @@ describe('useSuggestUserProfiles', () => { wrapper: appMockRender.AppWrapper, }); - jest.advanceTimersByTime(500); await waitFor(() => result.current.isSuccess); expect(spyOnSuggestUserProfiles).toBeCalledWith({ @@ -75,7 +61,6 @@ describe('useSuggestUserProfiles', () => { wrapper: appMockRender.AppWrapper, }); - jest.advanceTimersByTime(500); await waitFor(() => result.current.isError); expect(addError).toHaveBeenCalled(); diff --git a/x-pack/plugins/cases/public/containers/user_profiles/use_suggest_user_profiles.ts b/x-pack/plugins/cases/public/containers/user_profiles/use_suggest_user_profiles.ts index 3705224e1d0c8..6c83f853b2624 100644 --- a/x-pack/plugins/cases/public/containers/user_profiles/use_suggest_user_profiles.ts +++ b/x-pack/plugins/cases/public/containers/user_profiles/use_suggest_user_profiles.ts @@ -6,7 +6,7 @@ */ import { useState } from 'react'; -import { useQuery, UseQueryResult } from 'react-query'; +import { useQuery, UseQueryResult } from '@tanstack/react-query'; import useDebounce from 'react-use/lib/useDebounce'; import { UserProfile } from '@kbn/security-plugin/common'; import { DEFAULT_USER_SIZE } from '../../../common/constants'; diff --git a/x-pack/plugins/cloud_security_posture/common/types.ts b/x-pack/plugins/cloud_security_posture/common/types.ts index f79b651f826ca..f4d139d6a58da 100644 --- a/x-pack/plugins/cloud_security_posture/common/types.ts +++ b/x-pack/plugins/cloud_security_posture/common/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { PackagePolicy, GetAgentPoliciesResponseItem } from '@kbn/fleet-plugin/common'; +import type { PackagePolicy, AgentPolicy } from '@kbn/fleet-plugin/common'; import type { CspRuleMetadata } from './schemas/csp_rule_metadata'; export type Evaluation = 'passed' | 'failed' | 'NA'; @@ -49,7 +49,7 @@ export interface ComplianceDashboardData { trend: PostureTrend[]; } -export type Status = +export type CspStatusCode = | 'indexed' // latest findings index exists and has results | 'indexing' // index timeout was not surpassed since installation, assumes data is being indexed | 'index-timeout' // index timeout was surpassed since installation @@ -58,16 +58,16 @@ export type Status = interface BaseCspSetupStatus { latestPackageVersion: string; - installedIntegrations: number; + installedPackagePolicies: number; healthyAgents: number; } interface CspSetupNotInstalledStatus extends BaseCspSetupStatus { - status: Extract; + status: Extract; } interface CspSetupInstalledStatus extends BaseCspSetupStatus { - status: Exclude; + status: Exclude; // if installedPackageVersion == undefined but status != 'not-installed' it means the integration was installed in the past and findings were found // status can be `indexed` but return with undefined package information in this case installedPackageVersion: string | undefined; @@ -81,6 +81,8 @@ export interface CspRulesStatus { disabled: number; } +export type AgentPolicyStatus = Pick & { agents: number }; + export interface Benchmark { package_policy: Pick< PackagePolicy, @@ -94,7 +96,7 @@ export interface Benchmark { | 'created_at' | 'created_by' >; - agent_policy: Pick; + agent_policy: AgentPolicyStatus; rules: CspRulesStatus; } diff --git a/x-pack/plugins/cloud_security_posture/public/application/csp_router.tsx b/x-pack/plugins/cloud_security_posture/public/application/csp_router.tsx index 2fd84c5967e29..9f17ae174dd67 100644 --- a/x-pack/plugins/cloud_security_posture/public/application/csp_router.tsx +++ b/x-pack/plugins/cloud_security_posture/public/application/csp_router.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { QueryClient, QueryClientProvider } from 'react-query'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { Redirect, Route, RouteComponentProps, type RouteProps, Switch } from 'react-router-dom'; import { CLOUD_SECURITY_POSTURE_BASE_PATH, type CspSecuritySolutionContext } from '..'; import { cloudPosturePages } from '../common/navigation/constants'; diff --git a/x-pack/plugins/cloud_security_posture/public/common/api/use_cis_kubernetes_integration.tsx b/x-pack/plugins/cloud_security_posture/public/common/api/use_cis_kubernetes_integration.tsx index 5660080cb0a43..26b885ad44344 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/api/use_cis_kubernetes_integration.tsx +++ b/x-pack/plugins/cloud_security_posture/public/common/api/use_cis_kubernetes_integration.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { epmRouteService, type GetInfoResponse, diff --git a/x-pack/plugins/cloud_security_posture/public/common/api/use_compliance_dashboard_data_api.ts b/x-pack/plugins/cloud_security_posture/public/common/api/use_compliance_dashboard_data_api.ts index feb9cf50ad120..c1b108bb5f98a 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/api/use_compliance_dashboard_data_api.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/api/use_compliance_dashboard_data_api.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { type QueryObserverOptions, useQuery } from 'react-query'; +import { type QueryObserverOptions, useQuery } from '@tanstack/react-query'; import { useKibana } from '../hooks/use_kibana'; import { ComplianceDashboardData } from '../../../common/types'; import { STATS_ROUTE_PATH } from '../../../common/constants'; diff --git a/x-pack/plugins/cloud_security_posture/public/common/api/use_latest_findings_data_view.ts b/x-pack/plugins/cloud_security_posture/public/common/api/use_latest_findings_data_view.ts index 8f7a9c9b59d5e..e40a4c5a8e8c2 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/api/use_latest_findings_data_view.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/api/use_latest_findings_data_view.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import type { DataView } from '@kbn/data-plugin/common'; import { CSP_LATEST_FINDINGS_DATA_VIEW } from '../../../common/constants'; diff --git a/x-pack/plugins/cloud_security_posture/public/common/api/use_setup_status_api.ts b/x-pack/plugins/cloud_security_posture/public/common/api/use_setup_status_api.ts index fce0b9c2d7790..7c5d4eb8dc31b 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/api/use_setup_status_api.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/api/use_setup_status_api.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery, type UseQueryOptions } from 'react-query'; +import { useQuery, type UseQueryOptions } from '@tanstack/react-query'; import { useKibana } from '../hooks/use_kibana'; import { CspSetupStatus } from '../../../common/types'; import { STATUS_ROUTE_PATH } from '../../../common/constants'; diff --git a/x-pack/plugins/cloud_security_posture/public/components/cloud_posture_page.test.tsx b/x-pack/plugins/cloud_security_posture/public/components/cloud_posture_page.test.tsx index 3de37af1d7a65..c3cec5ca27774 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/cloud_posture_page.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/cloud_posture_page.test.tsx @@ -18,7 +18,7 @@ import { TestProvider } from '../test/test_provider'; import { coreMock } from '@kbn/core/public/mocks'; import { render, screen } from '@testing-library/react'; import React, { ComponentProps } from 'react'; -import { UseQueryResult } from 'react-query'; +import { UseQueryResult } from '@tanstack/react-query'; import { CloudPosturePage } from './cloud_posture_page'; import { NoDataPage } from '@kbn/kibana-react-plugin/public'; import { useCspSetupStatusApi } from '../common/api/use_setup_status_api'; diff --git a/x-pack/plugins/cloud_security_posture/public/components/cloud_posture_page.tsx b/x-pack/plugins/cloud_security_posture/public/components/cloud_posture_page.tsx index dfa1c51f5226a..4ded9cb9060bb 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/cloud_posture_page.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/cloud_posture_page.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; import { i18n } from '@kbn/i18n'; -import type { UseQueryResult } from 'react-query'; +import type { UseQueryResult } from '@tanstack/react-query'; import { EuiEmptyPrompt } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { NoDataPage } from '@kbn/kibana-react-plugin/public'; @@ -166,7 +166,7 @@ export const CloudPosturePage = ({ return defaultErrorRenderer(getSetupStatus.error); } - if (getSetupStatus.isLoading || getSetupStatus.isIdle) { + if (getSetupStatus.isLoading) { return defaultLoadingRenderer(); } @@ -182,7 +182,7 @@ export const CloudPosturePage = ({ return errorRender(query.error); } - if (query.isLoading || query.isIdle) { + if (query.isLoading) { return loadingRender(); } diff --git a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.test.tsx index 12ba2e9db3885..af10c55f19ea9 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import Chance from 'chance'; import { render, screen } from '@testing-library/react'; -import type { UseQueryResult } from 'react-query/types/react/types'; +import type { UseQueryResult } from '@tanstack/react-query'; import { createCspBenchmarkIntegrationFixture } from '../../test/fixtures/csp_benchmark_integration'; import { createReactQueryResponse } from '../../test/fixtures/react_query'; import { TestProvider } from '../../test/test_provider'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks_table.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks_table.test.tsx index 5fc9a12064153..463d19928db80 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks_table.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks_table.test.tsx @@ -65,7 +65,7 @@ describe('', () => { const agentPolicy = { id: chance.guid(), name: chance.sentence(), - number_of_agents: chance.integer({ min: 1 }), + agents: chance.integer({ min: 1 }), }; const benchmarks = [createCspBenchmarkIntegrationFixture({ agent_policy: agentPolicy })]; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/use_csp_benchmark_integrations.ts b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/use_csp_benchmark_integrations.ts index 0315893ef4cc2..ccdc3650b2596 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/use_csp_benchmark_integrations.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/use_csp_benchmark_integrations.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import type { ListResult } from '@kbn/fleet-plugin/common'; import { BENCHMARKS_ROUTE_PATH } from '../../../common/constants'; import type { BenchmarksQueryParams } from '../../../common/schemas/benchmark'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/es_pit/findings_es_pit_context.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/es_pit/findings_es_pit_context.ts index 54105ad21495f..aa1d660229d75 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/es_pit/findings_es_pit_context.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/es_pit/findings_es_pit_context.ts @@ -6,7 +6,7 @@ */ import { createContext, type MutableRefObject } from 'react'; -import type { UseQueryResult } from 'react-query'; +import type { UseQueryResult } from '@tanstack/react-query'; interface FindingsEsPitContextValue { setPitId(newPitId: string): void; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/es_pit/use_findings_es_pit.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/es_pit/use_findings_es_pit.ts index d7d7d6cbe3415..d8f2b22f501d4 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/es_pit/use_findings_es_pit.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/es_pit/use_findings_es_pit.ts @@ -6,7 +6,7 @@ */ import { useCallback, useRef, useState } from 'react'; -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { CSP_LATEST_FINDINGS_DATA_VIEW, ES_PIT_ROUTE_PATH } from '../../../../common/constants'; import { useKibana } from '../../../common/hooks/use_kibana'; import { FINDINGS_PIT_KEEP_ALIVE } from '../constants'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.test.tsx index 7cc01358b17d5..2ee05a2565adc 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.test.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; import Chance from 'chance'; -import type { UseQueryResult } from 'react-query'; +import type { UseQueryResult } from '@tanstack/react-query'; import { of } from 'rxjs'; import { useLatestFindingsDataView } from '../../common/api/use_latest_findings_data_view'; import { Findings } from './findings'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.tsx index baf4391eb2cc8..873b2c3c5cd76 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.tsx @@ -5,7 +5,7 @@ * 2.0. */ import React from 'react'; -import type { UseQueryResult } from 'react-query'; +import type { UseQueryResult } from '@tanstack/react-query'; import { Redirect, Switch, Route, useLocation } from 'react-router-dom'; import { useCspSetupStatusApi } from '../../common/api/use_setup_status_api'; import { NoFindingsStates } from '../../components/no_findings_states'; @@ -28,7 +28,7 @@ export const Findings = () => { if (!hasFindings) return ; let queryForCloudPosturePage: UseQueryResult = dataViewQuery; - if (pitQuery.isError || pitQuery.isLoading || pitQuery.isIdle) { + if (pitQuery.isError || pitQuery.isLoading) { queryForCloudPosturePage = pitQuery; } diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.test.tsx index 9dc42b2e4413c..4090f80435b46 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.test.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { UseQueryResult } from 'react-query'; +import type { UseQueryResult } from '@tanstack/react-query'; import { createReactQueryResponse } from '../../../test/fixtures/react_query'; import React from 'react'; import { render } from '@testing-library/react'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/use_latest_findings.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/use_latest_findings.ts index 2766ddbebb047..720d21cea2012 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/use_latest_findings.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/use_latest_findings.ts @@ -5,7 +5,7 @@ * 2.0. */ import { useContext } from 'react'; -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { number } from 'io-ts'; import { lastValueFrom } from 'rxjs'; import type { IKibanaSearchRequest, IKibanaSearchResponse } from '@kbn/data-plugin/common'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/use_resource_findings.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/use_resource_findings.ts index 44be336f1c041..53748c54eae0a 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/use_resource_findings.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/use_resource_findings.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { lastValueFrom } from 'rxjs'; import { IKibanaSearchRequest, IKibanaSearchResponse } from '@kbn/data-plugin/common'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/use_findings_by_resource.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/use_findings_by_resource.ts index ebb8f56c84e40..6def567d64ddd 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/use_findings_by_resource.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/use_findings_by_resource.ts @@ -5,7 +5,7 @@ * 2.0. */ import { useContext } from 'react'; -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { lastValueFrom } from 'rxjs'; import { IKibanaSearchRequest, IKibanaSearchResponse } from '@kbn/data-plugin/common'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules.test.tsx index 32fb45c9fe025..82238c2d7c4d0 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import Chance from 'chance'; import { Rules } from '.'; import { render, screen } from '@testing-library/react'; -import { QueryClient } from 'react-query'; +import { QueryClient } from '@tanstack/react-query'; import { TestProvider } from '../../test/test_provider'; import { useCspIntegrationInfo } from './use_csp_integration'; import { type RouteComponentProps } from 'react-router-dom'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.test.tsx index 64488f27d1353..bd22be3fab482 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { RulesContainer } from './rules_container'; import { render, screen, fireEvent, within } from '@testing-library/react'; -import { QueryClient } from 'react-query'; +import { QueryClient } from '@tanstack/react-query'; import { useFindCspRules, useBulkUpdateCspRules, type RuleSavedObject } from './use_csp_rules'; import * as TEST_SUBJECTS from './test_subjects'; import { Chance } from 'chance'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_integration.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_integration.tsx index d28f63769a615..3743127e9b9bd 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_integration.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_integration.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { type CopyAgentPolicyResponse, type GetOnePackagePolicyResponse, diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_rules.ts b/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_rules.ts index 8e9b74429b4a3..01ca13870964b 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_rules.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_rules.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { useQuery, useMutation, useQueryClient } from 'react-query'; +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { FunctionKeys } from 'utility-types'; import type { SavedObjectsFindOptions, SimpleSavedObject } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; @@ -74,10 +74,7 @@ export const useBulkUpdateCspRules = () => { }, onSettled: () => // Invalidate all queries for simplicity - queryClient.invalidateQueries({ - queryKey: CSP_RULE_SAVED_OBJECT_TYPE, - exact: false, - }), + queryClient.invalidateQueries([CSP_RULE_SAVED_OBJECT_TYPE]), } ); }; diff --git a/x-pack/plugins/cloud_security_posture/public/test/fixtures/react_query.ts b/x-pack/plugins/cloud_security_posture/public/test/fixtures/react_query.ts index 0169c359b26eb..9605515618882 100644 --- a/x-pack/plugins/cloud_security_posture/public/test/fixtures/react_query.ts +++ b/x-pack/plugins/cloud_security_posture/public/test/fixtures/react_query.ts @@ -5,10 +5,10 @@ * 2.0. */ -import type { UseQueryResult } from 'react-query/types/react/types'; +import type { UseQueryResult } from '@tanstack/react-query'; interface CreateReactQueryResponseInput { - status?: UseQueryResult['status']; + status?: UseQueryResult['status'] | 'idle'; data?: TData; error?: TError; } @@ -35,12 +35,12 @@ export const createReactQueryResponse = ({ if (status === 'idle') { return { - status, + status: 'loading', data: undefined, isSuccess: false, - isLoading: false, + isLoading: true, isError: false, - isIdle: true, + fetchStatus: 'idle', }; } diff --git a/x-pack/plugins/cloud_security_posture/public/test/test_provider.tsx b/x-pack/plugins/cloud_security_posture/public/test/test_provider.tsx index 758cdb8ca3f60..09c03c29750a7 100755 --- a/x-pack/plugins/cloud_security_posture/public/test/test_provider.tsx +++ b/x-pack/plugins/cloud_security_posture/public/test/test_provider.tsx @@ -9,7 +9,7 @@ import type { AppMountParameters, CoreStart } from '@kbn/core/public'; import React, { useMemo } from 'react'; import { I18nProvider } from '@kbn/i18n-react'; import { Router, Switch, Route } from 'react-router-dom'; -import { QueryClient, QueryClientProvider } from 'react-query'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { coreMock } from '@kbn/core/public/mocks'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; diff --git a/x-pack/plugins/cloud_security_posture/server/lib/is_latest_findings_index_exists.ts b/x-pack/plugins/cloud_security_posture/server/lib/check_for_findings.ts similarity index 69% rename from x-pack/plugins/cloud_security_posture/server/lib/is_latest_findings_index_exists.ts rename to x-pack/plugins/cloud_security_posture/server/lib/check_for_findings.ts index fda35a6d6e2ba..b8ca29e6512ae 100644 --- a/x-pack/plugins/cloud_security_posture/server/lib/is_latest_findings_index_exists.ts +++ b/x-pack/plugins/cloud_security_posture/server/lib/check_for_findings.ts @@ -6,15 +6,16 @@ */ import { ElasticsearchClient, type Logger } from '@kbn/core/server'; -import { LATEST_FINDINGS_INDEX_DEFAULT_NS } from '../../common/constants'; +import { LATEST_FINDINGS_INDEX_DEFAULT_NS, FINDINGS_INDEX_PATTERN } from '../../common/constants'; -export const isLatestFindingsIndexExists = async ( +export const checkForFindings = async ( esClient: ElasticsearchClient, + latestIndex: boolean, logger: Logger ): Promise => { try { const queryResult = await esClient.search({ - index: LATEST_FINDINGS_INDEX_DEFAULT_NS, + index: latestIndex ? LATEST_FINDINGS_INDEX_DEFAULT_NS : FINDINGS_INDEX_PATTERN, query: { match_all: {}, }, @@ -23,7 +24,7 @@ export const isLatestFindingsIndexExists = async ( return !!queryResult.hits.hits.length; } catch (e) { - logger.error(e.message); + logger.debug(e); return false; } }; diff --git a/x-pack/plugins/cloud_security_posture/server/lib/fleet_util.ts b/x-pack/plugins/cloud_security_posture/server/lib/fleet_util.ts index e482f0af210a4..42e11f08d9d80 100644 --- a/x-pack/plugins/cloud_security_posture/server/lib/fleet_util.ts +++ b/x-pack/plugins/cloud_security_posture/server/lib/fleet_util.ts @@ -12,7 +12,7 @@ import type { AgentService, } from '@kbn/fleet-plugin/server'; import type { - GetAgentPoliciesResponseItem, + GetAgentStatusResponse, PackagePolicy, AgentPolicy, ListResult, @@ -33,22 +33,24 @@ const getPackageNameQuery = (packageName: string, benchmarkFilter?: string): str return kquery; }; -export const addRunningAgentToAgentPolicy = async ( +export type AgentStatusByAgentPolicyMap = Record; + +export const getAgentStatusesByAgentPolicies = async ( agentService: AgentService, - agentPolicies: AgentPolicy[] -): Promise => { - if (!agentPolicies.length) return []; + agentPolicies: AgentPolicy[] | undefined +): Promise => { + if (!agentPolicies?.length) return {}; + + const internalAgentService = agentService.asInternalUser; + const result: AgentStatusByAgentPolicyMap = {}; + + for (const agentPolicy of agentPolicies) { + result[agentPolicy.id] = await internalAgentService.getAgentStatusForAgentPolicy( + agentPolicy.id + ); + } - return Promise.all( - agentPolicies.map((agentPolicy) => - agentService.asInternalUser - .getAgentStatusForAgentPolicy(agentPolicy.id) - .then((agentStatus) => ({ - ...agentPolicy, - agents: agentStatus.total, - })) - ) - ); + return result; }; export const getCspAgentPolicies = async ( diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts index 258d5c4fdf136..a950741ea2d8f 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts @@ -312,7 +312,6 @@ describe('benchmarks API', () => { it('should build benchmark entry agent policy and package policy', async () => { const packagePolicy = createPackagePolicyMock(); const agentPolicy = createMockAgentPolicy(); - agentPolicy.agents = 3; const cspRulesStatus = { all: 100, @@ -320,7 +319,11 @@ describe('benchmarks API', () => { disabled: 48, }; const enrichAgentPolicy = await createBenchmarkEntry( - agentPolicy, + { + id: agentPolicy.id, + name: agentPolicy.name, + agents: 3, + }, packagePolicy, cspRulesStatus ); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts index ef4314d542626..8ffeb38b6b576 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts @@ -6,14 +6,14 @@ */ import type { SavedObjectsClientContract, SavedObjectsFindResponse } from '@kbn/core/server'; import { transformError } from '@kbn/securitysolution-es-utils'; -import type { GetAgentPoliciesResponseItem, PackagePolicy } from '@kbn/fleet-plugin/common'; +import type { AgentPolicy, PackagePolicy } from '@kbn/fleet-plugin/common'; import { BENCHMARKS_ROUTE_PATH, CLOUD_SECURITY_POSTURE_PACKAGE_NAME, CSP_RULE_SAVED_OBJECT_TYPE, } from '../../../common/constants'; import { benchmarksQueryParamsSchema } from '../../../common/schemas/benchmark'; -import type { Benchmark, CspRulesStatus } from '../../../common/types'; +import type { Benchmark, CspRulesStatus, AgentPolicyStatus } from '../../../common/types'; import type { CspRule } from '../../../common/schemas'; import { createCspRuleSearchFilterByPackagePolicy, @@ -21,7 +21,8 @@ import { } from '../../../common/utils/helpers'; import { CspRouter } from '../../types'; import { - addRunningAgentToAgentPolicy, + getAgentStatusesByAgentPolicies, + type AgentStatusByAgentPolicyMap, getCspAgentPolicies, getCspPackagePolicies, } from '../../lib/fleet_util'; @@ -74,7 +75,7 @@ export const addPackagePolicyCspRules = async ( }; export const createBenchmarkEntry = ( - agentPolicy: GetAgentPoliciesResponseItem, + agentPolicyStatus: AgentPolicyStatus, packagePolicy: PackagePolicy, cspRulesStatus: CspRulesStatus ): Benchmark => ({ @@ -95,17 +96,14 @@ export const createBenchmarkEntry = ( } : undefined, }, - agent_policy: { - id: agentPolicy.id, - name: agentPolicy.name, - agents: agentPolicy.agents, - }, + agent_policy: agentPolicyStatus, rules: cspRulesStatus, }); const createBenchmarks = ( soClient: SavedObjectsClientContract, - agentPolicies: GetAgentPoliciesResponseItem[], + agentPolicies: AgentPolicy[], + agentStatusByAgentPolicyId: AgentStatusByAgentPolicyMap, cspPackagePolicies: PackagePolicy[] ): Promise => { const cspPackagePoliciesMap = new Map( @@ -120,7 +118,12 @@ const createBenchmarks = ( .filter(isNonNullable); const benchmarks = cspPackagesOnAgent.map(async (cspPackage) => { const cspRulesStatus = await addPackagePolicyCspRules(soClient, cspPackage); - const benchmark = createBenchmarkEntry(agentPolicy, cspPackage, cspRulesStatus); + const agentPolicyStatus = { + id: agentPolicy.id, + name: agentPolicy.name, + agents: agentStatusByAgentPolicyId[agentPolicy.id].total, + }; + const benchmark = createBenchmarkEntry(agentPolicyStatus, cspPackage, cspRulesStatus); return benchmark; }); @@ -159,14 +162,15 @@ export const defineGetBenchmarksRoute = (router: CspRouter): void => cspContext.agentPolicyService ); - const enrichAgentPolicies = await addRunningAgentToAgentPolicy( + const agentStatusesByAgentPolicyId = await getAgentStatusesByAgentPolicies( cspContext.agentService, agentPolicies ); const benchmarks = await createBenchmarks( cspContext.soClient, - enrichAgentPolicies, + agentPolicies, + agentStatusesByAgentPolicyId, cspPackagePolicies.items ); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.ts b/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.ts index 463bdab0cdb6e..6c6358e772e6a 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.ts @@ -24,8 +24,8 @@ import { createCspRuleSearchFilterByPackagePolicy } from '../../../common/utils/ import type { CspRule, CspRulesConfiguration } from '../../../common/schemas'; import { CLOUD_SECURITY_POSTURE_PACKAGE_NAME, - UPDATE_RULES_CONFIG_ROUTE_PATH, CSP_RULE_SAVED_OBJECT_TYPE, + UPDATE_RULES_CONFIG_ROUTE_PATH, } from '../../../common/constants'; import { CspRouter } from '../../types'; @@ -38,7 +38,7 @@ export const getPackagePolicy = async ( // PackagePolicies always contains one element, even when package does not exist if (!packagePolicies || !packagePolicies[0].version) { - throw new Error(`package policy Id '${packagePolicyId}' is not exist`); + throw new Error(`Package policy Id '${packagePolicyId}' does not exist`); } if (packagePolicies[0].package?.name !== CLOUD_SECURITY_POSTURE_PACKAGE_NAME) { throw new Error( diff --git a/x-pack/plugins/cloud_security_posture/server/routes/setup_routes.ts b/x-pack/plugins/cloud_security_posture/server/routes/setup_routes.ts index 03b42600b9261..436cd9eb7a526 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/setup_routes.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/setup_routes.ts @@ -16,7 +16,7 @@ import { PLUGIN_ID } from '../../common'; import { defineGetComplianceDashboardRoute } from './compliance_dashboard/compliance_dashboard'; import { defineGetBenchmarksRoute } from './benchmarks/benchmarks'; import { defineUpdateRulesConfigRoute } from './configuration/update_rules_configuration'; -import { defineGetCspSetupStatusRoute } from './status/status'; +import { defineGetCspStatusRoute } from './status/status'; import { defineEsPitRoute } from './es_pit/es_pit'; /** @@ -34,7 +34,7 @@ export function setupRoutes({ defineGetComplianceDashboardRoute(router); defineGetBenchmarksRoute(router); defineUpdateRulesConfigRoute(router); - defineGetCspSetupStatusRoute(router); + defineGetCspStatusRoute(router); defineEsPitRoute(router); core.http.registerRouteHandlerContext( diff --git a/x-pack/plugins/cloud_security_posture/server/routes/status/status.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/status/status.test.ts index a67448f05a5ad..a066b3b409054 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/status/status.test.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/status/status.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { defineGetCspSetupStatusRoute, INDEX_TIMEOUT_IN_MINUTES } from './status'; +import { defineGetCspStatusRoute, INDEX_TIMEOUT_IN_MINUTES } from './status'; import { httpServerMock, httpServiceMock } from '@kbn/core/server/mocks'; import type { ESSearchResponse } from '@kbn/core/types/elasticsearch'; import { @@ -79,7 +79,7 @@ describe('CspSetupStatus route', () => { }); it('validate the API route path', async () => { - defineGetCspSetupStatusRoute(router); + defineGetCspStatusRoute(router); const [config, _] = router.get.mock.calls[0]; expect(config.path).toEqual('/internal/cloud_security_posture/status'); @@ -101,7 +101,7 @@ describe('CspSetupStatus route', () => { }); // Act - defineGetCspSetupStatusRoute(router); + defineGetCspStatusRoute(router); const [_, handler] = router.get.mock.calls[0]; const mockResponse = httpServerMock.createResponseFactory(); @@ -116,7 +116,7 @@ describe('CspSetupStatus route', () => { await expect(body).toEqual({ status: 'indexed', latestPackageVersion: '0.0.14', - installedIntegrations: 0, + installedPackagePolicies: 0, healthyAgents: 0, installedPackageVersion: undefined, }); @@ -140,7 +140,7 @@ describe('CspSetupStatus route', () => { }); // Act - defineGetCspSetupStatusRoute(router); + defineGetCspStatusRoute(router); const [_, handler] = router.get.mock.calls[0]; const mockResponse = httpServerMock.createResponseFactory(); @@ -156,7 +156,7 @@ describe('CspSetupStatus route', () => { await expect(body).toEqual({ status: 'indexed', latestPackageVersion: '0.0.14', - installedIntegrations: 3, + installedPackagePolicies: 3, healthyAgents: 0, installedPackageVersion: '0.0.14', }); @@ -188,7 +188,7 @@ describe('CspSetupStatus route', () => { } as unknown as GetAgentStatusResponse['results']); // Act - defineGetCspSetupStatusRoute(router); + defineGetCspStatusRoute(router); const [_, handler] = router.get.mock.calls[0]; const mockResponse = httpServerMock.createResponseFactory(); @@ -204,7 +204,7 @@ describe('CspSetupStatus route', () => { await expect(body).toEqual({ status: 'indexed', latestPackageVersion: '0.0.14', - installedIntegrations: 3, + installedPackagePolicies: 3, healthyAgents: 1, installedPackageVersion: '0.0.14', }); @@ -224,13 +224,13 @@ describe('CspSetupStatus route', () => { page: 1, perPage: 100, }); - - // Act - defineGetCspSetupStatusRoute(router); + defineGetCspStatusRoute(router); const [_, handler] = router.get.mock.calls[0]; const mockResponse = httpServerMock.createResponseFactory(); const mockRequest = httpServerMock.createKibanaRequest(); + + // Act await handler(mockContext, mockRequest, mockResponse); // Assert @@ -242,7 +242,7 @@ describe('CspSetupStatus route', () => { await expect(body).toMatchObject({ status: 'not-installed', latestPackageVersion: '0.0.14', - installedIntegrations: 0, + installedPackagePolicies: 0, healthyAgents: 0, }); }); @@ -273,7 +273,7 @@ describe('CspSetupStatus route', () => { } as unknown as GetAgentStatusResponse['results']); // Act - defineGetCspSetupStatusRoute(router); + defineGetCspStatusRoute(router); const [_, handler] = router.get.mock.calls[0]; @@ -290,7 +290,7 @@ describe('CspSetupStatus route', () => { await expect(body).toMatchObject({ status: 'not-deployed', latestPackageVersion: '0.0.14', - installedIntegrations: 1, + installedPackagePolicies: 1, healthyAgents: 0, installedPackageVersion: '0.0.14', }); @@ -327,7 +327,7 @@ describe('CspSetupStatus route', () => { } as unknown as GetAgentStatusResponse['results']); // Act - defineGetCspSetupStatusRoute(router); + defineGetCspStatusRoute(router); const [_, handler] = router.get.mock.calls[0]; @@ -346,7 +346,7 @@ describe('CspSetupStatus route', () => { await expect(body).toMatchObject({ status: 'indexing', latestPackageVersion: '0.0.14', - installedIntegrations: 1, + installedPackagePolicies: 1, healthyAgents: 1, installedPackageVersion: '0.0.14', }); @@ -383,7 +383,7 @@ describe('CspSetupStatus route', () => { } as unknown as GetAgentStatusResponse['results']); // Act - defineGetCspSetupStatusRoute(router); + defineGetCspStatusRoute(router); const [_, handler] = router.get.mock.calls[0]; @@ -401,7 +401,7 @@ describe('CspSetupStatus route', () => { await expect(body).toMatchObject({ status: 'index-timeout', latestPackageVersion: '0.0.14', - installedIntegrations: 1, + installedPackagePolicies: 1, healthyAgents: 1, installedPackageVersion: '0.0.14', }); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/status/status.ts b/x-pack/plugins/cloud_security_posture/server/routes/status/status.ts index b7deefff368ed..a85d64e22c994 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/status/status.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/status/status.ts @@ -6,123 +6,119 @@ */ import { transformError } from '@kbn/securitysolution-es-utils'; -import type { Logger, ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; -import type { - AgentPolicyServiceInterface, - AgentService, - PackagePolicyServiceInterface, - PackageService, -} from '@kbn/fleet-plugin/server'; -import moment, { MomentInput } from 'moment'; +import type { SavedObjectsClientContract } from '@kbn/core/server'; +import type { AgentPolicyServiceInterface, AgentService } from '@kbn/fleet-plugin/server'; +import moment from 'moment'; import { PackagePolicy } from '@kbn/fleet-plugin/common'; import { CLOUD_SECURITY_POSTURE_PACKAGE_NAME, STATUS_ROUTE_PATH } from '../../../common/constants'; -import type { CspRouter } from '../../types'; -import type { CspSetupStatus, Status } from '../../../common/types'; +import type { CspApiRequestHandlerContext, CspRouter } from '../../types'; +import type { CspSetupStatus, CspStatusCode } from '../../../common/types'; import { - addRunningAgentToAgentPolicy, + getAgentStatusesByAgentPolicies, getCspAgentPolicies, getCspPackagePolicies, } from '../../lib/fleet_util'; -import { isLatestFindingsIndexExists } from '../../lib/is_latest_findings_index_exists'; +import { checkForFindings } from '../../lib/check_for_findings'; export const INDEX_TIMEOUT_IN_MINUTES = 10; -// this function currently returns all agents instead of healthy agents only +const calculateDiffFromNowInMinutes = (date: string | number): number => + moment().diff(moment(date), 'minutes'); + const getHealthyAgents = async ( soClient: SavedObjectsClientContract, - installedIntegrations: PackagePolicy[], + installedCspPackagePolicies: PackagePolicy[], agentPolicyService: AgentPolicyServiceInterface, agentService: AgentService ): Promise => { + // Get agent policies of package policies (from installed package policies) const agentPolicies = await getCspAgentPolicies( soClient, - installedIntegrations, + installedCspPackagePolicies, agentPolicyService ); - const enrichedAgentPolicies = await addRunningAgentToAgentPolicy( + // Get agents statuses of the following agent policies + const agentStatusesByAgentPolicyId = await getAgentStatusesByAgentPolicies( agentService, - agentPolicies || [] + agentPolicies ); - return enrichedAgentPolicies.reduce( - (previousValue, currentValue) => previousValue + (currentValue.agents || 0), - 0 - ); + // TODO: should be fixed - currently returns all agents instead of healthy agents only + return Object.values(agentStatusesByAgentPolicyId).reduce((sum, status) => sum + status.total, 0); }; -const getMinutesPassedSinceMoment = (momentInput: MomentInput): number => - moment().diff(moment(momentInput), 'minutes'); - -const getStatus = ( - findingsIndexExists: boolean, - installedIntegrations: number, +const calculateCspStatusCode = ( + hasFindings: boolean, + installedCspPackagePolicies: number, healthyAgents: number, - minutesPassedSinceInstallation: number -): Status => { - if (findingsIndexExists) return 'indexed'; - if (installedIntegrations === 0) return 'not-installed'; + timeSinceInstallationInMinutes: number +): CspStatusCode => { + if (hasFindings) return 'indexed'; + if (installedCspPackagePolicies === 0) return 'not-installed'; if (healthyAgents === 0) return 'not-deployed'; - if (minutesPassedSinceInstallation <= INDEX_TIMEOUT_IN_MINUTES) return 'indexing'; - if (minutesPassedSinceInstallation > INDEX_TIMEOUT_IN_MINUTES) return 'index-timeout'; + if (timeSinceInstallationInMinutes <= INDEX_TIMEOUT_IN_MINUTES) return 'indexing'; + if (timeSinceInstallationInMinutes > INDEX_TIMEOUT_IN_MINUTES) return 'index-timeout'; - throw new Error('Could not determine csp setup status'); + throw new Error('Could not determine csp status'); }; -const getCspSetupStatus = async ( - logger: Logger, - esClient: ElasticsearchClient, - soClient: SavedObjectsClientContract, - packageService: PackageService, - packagePolicyService: PackagePolicyServiceInterface, - agentPolicyService: AgentPolicyServiceInterface, - agentService: AgentService -): Promise => { - const [findingsIndexExists, installationPackageInfo, latestPackageInfo, installedIntegrations] = - await Promise.all([ - isLatestFindingsIndexExists(esClient, logger), +const getCspStatus = async ({ + logger, + esClient, + soClient, + packageService, + packagePolicyService, + agentPolicyService, + agentService, +}: CspApiRequestHandlerContext): Promise => { + const [hasFindings, installation, latestCspPackage, installedPackagePolicies] = await Promise.all( + [ + checkForFindings(esClient.asCurrentUser, true, logger), packageService.asInternalUser.getInstallation(CLOUD_SECURITY_POSTURE_PACKAGE_NAME), packageService.asInternalUser.fetchFindLatestPackage(CLOUD_SECURITY_POSTURE_PACKAGE_NAME), getCspPackagePolicies(soClient, packagePolicyService, CLOUD_SECURITY_POSTURE_PACKAGE_NAME, { per_page: 10000, }), - ]); + ] + ); const healthyAgents = await getHealthyAgents( soClient, - installedIntegrations.items, + installedPackagePolicies.items, agentPolicyService, agentService ); - const installedIntegrationsTotal = installedIntegrations.total; - const latestPackageVersion = latestPackageInfo.version; + const installedPackagePoliciesTotal = installedPackagePolicies.total; + const latestCspPackageVersion = latestCspPackage.version; - const status = getStatus( - findingsIndexExists, - installedIntegrationsTotal, + const MIN_DATE = 0; + const status = calculateCspStatusCode( + hasFindings, + installedPackagePoliciesTotal, healthyAgents, - getMinutesPassedSinceMoment(installationPackageInfo?.install_started_at || 0) + calculateDiffFromNowInMinutes(installation?.install_started_at || MIN_DATE) ); if (status === 'not-installed') return { status, - latestPackageVersion, + latestPackageVersion: latestCspPackageVersion, healthyAgents, - installedIntegrations: installedIntegrationsTotal, + installedPackagePolicies: installedPackagePoliciesTotal, }; return { status, - latestPackageVersion, + latestPackageVersion: latestCspPackageVersion, healthyAgents, - installedIntegrations: installedIntegrationsTotal, - installedPackageVersion: installationPackageInfo?.install_version, + installedPackagePolicies: installedPackagePoliciesTotal, + installedPackageVersion: installation?.install_version, }; }; -export const defineGetCspSetupStatusRoute = (router: CspRouter): void => +export const defineGetCspStatusRoute = (router: CspRouter): void => router.get( { path: STATUS_ROUTE_PATH, @@ -134,25 +130,15 @@ export const defineGetCspSetupStatusRoute = (router: CspRouter): void => async (context, _, response) => { const cspContext = await context.csp; try { - const cspSetupStatus = await getCspSetupStatus( - cspContext.logger, - cspContext.esClient.asCurrentUser, - cspContext.soClient, - cspContext.packageService, - cspContext.packagePolicyService, - cspContext.agentPolicyService, - cspContext.agentService - ); - - const body: CspSetupStatus = cspSetupStatus; - + const status = await getCspStatus(cspContext); return response.ok({ - body, + body: status, }); } catch (err) { - const error = transformError(err); - cspContext.logger.error(`Error while fetching status: ${err}`); + cspContext.logger.error(`Error getting csp status`); + cspContext.logger.error(err); + const error = transformError(err); return response.customError({ body: { message: error.message }, statusCode: error.statusCode, diff --git a/x-pack/plugins/cloud_security_posture/server/types.ts b/x-pack/plugins/cloud_security_posture/server/types.ts index ab77f1090552a..43f47585d09f6 100644 --- a/x-pack/plugins/cloud_security_posture/server/types.ts +++ b/x-pack/plugins/cloud_security_posture/server/types.ts @@ -54,7 +54,7 @@ export type CspServerPluginStartServices = Promise< [CoreStart, CspServerPluginStartDeps, CspServerPluginStart] >; -interface CspApiRequestHandlerContext { +export interface CspApiRequestHandlerContext { user: ReturnType; logger: Logger; esClient: IScopedClusterClient; diff --git a/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.test.ts b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.test.ts index 724178f9b7088..75dd3fdfe8dce 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.test.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.test.ts @@ -15,11 +15,12 @@ import { EncryptionErrorOperation } from '../crypto/encryption_error'; import { encryptedSavedObjectsServiceMock } from '../crypto/index.mock'; import { EncryptedSavedObjectsClientWrapper } from './encrypted_saved_objects_client_wrapper'; -jest.mock('@kbn/core/server/saved_objects/service/lib/utils', () => { - const { SavedObjectsUtils } = jest.requireActual( - '@kbn/core/server/saved_objects/service/lib/utils' +jest.mock('@kbn/core-saved-objects-utils-server', () => { + const { SavedObjectsUtils, ...actual } = jest.requireActual( + '@kbn/core-saved-objects-utils-server' ); return { + ...actual, SavedObjectsUtils: { namespaceStringToId: SavedObjectsUtils.namespaceStringToId, isRandomId: SavedObjectsUtils.isRandomId, @@ -699,6 +700,7 @@ describe('#bulkUpdate', () => { expectOptionsNamespaceInDescriptor: boolean; expectObjectNamespaceInDescriptor: boolean; } + const doTest = async ({ optionsNamespace, objectNamespace, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/index/delete_index_api_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/index/delete_index_api_logic.test.ts new file mode 100644 index 0000000000000..6eaa5b3e95cf2 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/index/delete_index_api_logic.test.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { mockHttpValues } from '../../../__mocks__/kea_logic'; + +import { nextTick } from '@kbn/test-jest-helpers'; + +import { deleteIndex } from './delete_index_api_logic'; + +describe('deleteIndexApiLogic', () => { + const { http } = mockHttpValues; + beforeEach(() => { + jest.clearAllMocks(); + }); + describe('deleteIndex', () => { + it('calls correct api', async () => { + const promise = Promise.resolve(); + http.post.mockReturnValue(promise); + const result = deleteIndex({ indexName: 'deleteIndex' }); + await nextTick(); + expect(http.delete).toHaveBeenCalledWith('/internal/enterprise_search/indices/deleteIndex'); + await expect(result).resolves; + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/index/delete_index_api_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/index/delete_index_api_logic.ts new file mode 100644 index 0000000000000..ff92e4ecef6bc --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/index/delete_index_api_logic.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createApiLogic } from '../../../shared/api_logic/create_api_logic'; +import { HttpLogic } from '../../../shared/http'; + +export interface DeleteIndexApiLogicArgs { + indexName: string; +} + +export const deleteIndex = async ({ indexName }: DeleteIndexApiLogicArgs): Promise => { + const route = `/internal/enterprise_search/indices/${indexName}`; + await HttpLogic.values.http.delete(route); + return; +}; + +export const DeleteIndexApiLogic = createApiLogic(['delete_index_api_logic'], deleteIndex); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/index/fetch_indices_api_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/index/fetch_indices_api_logic.test.ts index 13b75e7c534be..42d78b65df449 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/index/fetch_indices_api_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/index/fetch_indices_api_logic.test.ts @@ -28,7 +28,12 @@ describe('FetchIndicesApiLogic', () => { expect(http.get).toHaveBeenCalledWith('/internal/enterprise_search/indices', { query: { page: 1, return_hidden_indices: false, search_query: null, size: 20 }, }); - await expect(result).resolves.toEqual({ isInitialRequest: true, result: 'result' }); + await expect(result).resolves.toEqual({ + isInitialRequest: true, + result: 'result', + returnHiddenIndices: false, + searchQuery: undefined, + }); }); it('sets initialRequest to false if page is not the first page', async () => { const promise = Promise.resolve({ result: 'result' }); @@ -41,7 +46,12 @@ describe('FetchIndicesApiLogic', () => { expect(http.get).toHaveBeenCalledWith('/internal/enterprise_search/indices', { query: { page: 2, return_hidden_indices: false, search_query: null, size: 20 }, }); - await expect(result).resolves.toEqual({ isInitialRequest: false, result: 'result' }); + await expect(result).resolves.toEqual({ + isInitialRequest: false, + result: 'result', + returnHiddenIndices: false, + searchQuery: undefined, + }); }); it('sets initialRequest to false if searchQuery is not empty', async () => { const promise = Promise.resolve({ result: 'result' }); @@ -55,7 +65,12 @@ describe('FetchIndicesApiLogic', () => { expect(http.get).toHaveBeenCalledWith('/internal/enterprise_search/indices', { query: { page: 1, return_hidden_indices: false, search_query: 'a', size: 20 }, }); - await expect(result).resolves.toEqual({ isInitialRequest: false, result: 'result' }); + await expect(result).resolves.toEqual({ + isInitialRequest: false, + result: 'result', + returnHiddenIndices: false, + searchQuery: 'a', + }); }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/index/fetch_indices_api_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/index/fetch_indices_api_logic.ts index a1ca3a9c3141f..709c47ed919c0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/index/fetch_indices_api_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/index/fetch_indices_api_logic.ts @@ -38,7 +38,7 @@ export const fetchIndices = async ({ // We need this to determine whether to show the empty state on the indices page const isInitialRequest = meta.page.current === 1 && !searchQuery; - return { ...response, isInitialRequest }; + return { ...response, isInitialRequest, returnHiddenIndices, searchQuery }; }; export const FetchIndicesAPILogic = createApiLogic(['content', 'indices_api_logic'], fetchIndices); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/layout/page_template.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/layout/page_template.test.tsx index b3aa172dd5bc0..d7e0f4c2b20e3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/layout/page_template.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/layout/page_template.test.tsx @@ -13,7 +13,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { SetEnterpriseSearchChrome } from '../../../shared/kibana_chrome'; +import { SetEnterpriseSearchContentChrome } from '../../../shared/kibana_chrome'; import { EnterpriseSearchPageTemplateWrapper } from '../../../shared/layout'; import { SendEnterpriseSearchTelemetry } from '../../../shared/telemetry'; @@ -39,7 +39,7 @@ describe('EnterpriseSearchContentPageTemplate', () => { .find(EnterpriseSearchPageTemplateWrapper) .prop('setPageChrome') as any; - expect(setPageChrome.type).toEqual(SetEnterpriseSearchChrome); + expect(setPageChrome.type).toEqual(SetEnterpriseSearchContentChrome); expect(setPageChrome.props.trail).toEqual(['Some page']); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/layout/page_template.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/layout/page_template.tsx index d512175f2842e..0bc6dc03c4388 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/layout/page_template.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/layout/page_template.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../../../../common/constants'; -import { SetEnterpriseSearchChrome } from '../../../shared/kibana_chrome'; +import { SetEnterpriseSearchContentChrome } from '../../../shared/kibana_chrome'; import { EnterpriseSearchPageTemplateWrapper, PageTemplateProps } from '../../../shared/layout'; import { useEnterpriseSearchNav } from '../../../shared/layout'; import { SendEnterpriseSearchTelemetry } from '../../../shared/telemetry'; @@ -27,7 +27,7 @@ export const EnterpriseSearchContentPageTemplate: React.FC = name: ENTERPRISE_SEARCH_CONTENT_PLUGIN.NAME, }} restrictWidth - setPageChrome={pageChrome && } + setPageChrome={pageChrome && } > {pageViewTelemetry && ( diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/delete_index_modal.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/delete_index_modal.tsx new file mode 100644 index 0000000000000..6dbb44d658b8c --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/delete_index_modal.tsx @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { useActions, useValues } from 'kea'; + +import { EuiConfirmModal } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { ingestionMethodToText } from '../../utils/indices'; + +import { IndicesLogic } from './indices_logic'; + +export const DeleteIndexModal: React.FC = () => { + const { closeDeleteModal, deleteIndex } = useActions(IndicesLogic); + const { + deleteModalIndexName: indexName, + deleteModalIngestionMethod: ingestionMethod, + isDeleteModalVisible, + } = useValues(IndicesLogic); + return isDeleteModalVisible ? ( + { + closeDeleteModal(); + }} + onConfirm={() => { + deleteIndex({ indexName }); + }} + cancelButtonText={i18n.translate( + 'xpack.enterpriseSearch.content.searchIndices.deleteModal.cancelButton.title', + { + defaultMessage: 'Cancel', + } + )} + confirmButtonText={i18n.translate( + 'xpack.enterpriseSearch.content.searchIndices.deleteModal.confirmButton.title', + { + defaultMessage: 'Delete index', + } + )} + defaultFocusedButton="confirm" + buttonColor="danger" + > +

+ {i18n.translate( + 'xpack.enterpriseSearch.content.searchIndices.deleteModal.delete.description', + { + defaultMessage: + 'Deleting this index will also delete all of its data and its {ingestionMethod} configuration. Any associated search engines will no longer be able to access any data stored in this index.This can not be undone.', + values: { + ingestionMethod: ingestionMethodToText(ingestionMethod), + }, + } + )} +

+
+ ) : ( + <> + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_logic.test.ts index 36157301d3bae..a636199dd1e47 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_logic.test.ts @@ -22,17 +22,22 @@ import { DEFAULT_META } from '../../../shared/constants'; import { FetchIndicesAPILogic } from '../../api/index/fetch_indices_api_logic'; -import { IngestionStatus } from '../../types'; +import { IngestionMethod, IngestionStatus } from '../../types'; import { IndicesLogic } from './indices_logic'; const DEFAULT_VALUES = { data: undefined, + deleteModalIndex: null, + deleteModalIndexName: '', + deleteModalIngestionMethod: IngestionMethod.API, hasNoIndices: false, indices: [], + isDeleteModalVisible: false, isFirstRequest: true, isLoading: true, meta: DEFAULT_META, + searchParams: { meta: DEFAULT_META, returnHiddenIndices: false }, status: Status.IDLE, }; @@ -64,11 +69,77 @@ describe('IndicesLogic', () => { current: 3, }, }, + searchParams: { + ...DEFAULT_VALUES.searchParams, + meta: { page: { ...DEFAULT_META.page, current: 3 } }, + }, }); }); }); + describe('openDeleteModal', () => { + it('should set deleteIndexName and set isDeleteModalVisible to true', () => { + IndicesLogic.actions.openDeleteModal(connectorIndex); + expect(IndicesLogic.values).toEqual({ + ...DEFAULT_VALUES, + deleteModalIndex: connectorIndex, + deleteModalIndexName: 'connector', + deleteModalIngestionMethod: IngestionMethod.CONNECTOR, + isDeleteModalVisible: true, + }); + }); + }); + describe('closeDeleteModal', () => { + it('should set deleteIndexName to empty and set isDeleteModalVisible to false', () => { + IndicesLogic.actions.openDeleteModal(connectorIndex); + IndicesLogic.actions.closeDeleteModal(); + expect(IndicesLogic.values).toEqual(DEFAULT_VALUES); + }); + }); }); describe('reducers', () => { + describe('isFirstRequest', () => { + it('should update to true on setIsFirstRequest', () => { + IndicesLogic.actions.setIsFirstRequest(); + expect(IndicesLogic.values).toEqual({ ...DEFAULT_VALUES, isFirstRequest: true }); + }); + it('should update to false on apiError', () => { + IndicesLogic.actions.setIsFirstRequest(); + IndicesLogic.actions.apiError({} as HttpError); + + expect(IndicesLogic.values).toEqual({ + ...DEFAULT_VALUES, + hasNoIndices: false, + indices: [], + isFirstRequest: false, + isLoading: false, + status: Status.ERROR, + }); + }); + it('should update to false on apiSuccess', () => { + IndicesLogic.actions.setIsFirstRequest(); + IndicesLogic.actions.apiSuccess({ + indices: [], + isInitialRequest: false, + meta: DEFAULT_VALUES.meta, + returnHiddenIndices: false, + }); + + expect(IndicesLogic.values).toEqual({ + ...DEFAULT_VALUES, + data: { + indices: [], + isInitialRequest: false, + meta: DEFAULT_VALUES.meta, + returnHiddenIndices: false, + }, + hasNoIndices: false, + indices: [], + isFirstRequest: false, + isLoading: false, + status: Status.SUCCESS, + }); + }); + }); describe('meta', () => { it('updates when apiSuccess listener triggered', () => { const newMeta = { @@ -84,18 +155,28 @@ describe('IndicesLogic', () => { indices, isInitialRequest: true, meta: newMeta, + returnHiddenIndices: true, + searchQuery: 'a', }); expect(IndicesLogic.values).toEqual({ + ...DEFAULT_VALUES, data: { indices, isInitialRequest: true, meta: newMeta, + returnHiddenIndices: true, + searchQuery: 'a', }, hasNoIndices: false, indices: elasticsearchViewIndices, isFirstRequest: false, isLoading: false, meta: newMeta, + searchParams: { + meta: newMeta, + returnHiddenIndices: true, + searchQuery: 'a', + }, status: Status.SUCCESS, }); }); @@ -115,18 +196,25 @@ describe('IndicesLogic', () => { indices: [], isInitialRequest: true, meta, + returnHiddenIndices: false, }); expect(IndicesLogic.values).toEqual({ + ...DEFAULT_VALUES, data: { indices: [], isInitialRequest: true, meta, + returnHiddenIndices: false, }, hasNoIndices: true, indices: [], isFirstRequest: false, isLoading: false, meta, + searchParams: { + ...DEFAULT_VALUES.searchParams, + meta, + }, status: Status.SUCCESS, }); }); @@ -144,18 +232,25 @@ describe('IndicesLogic', () => { indices: [], isInitialRequest: false, meta, + returnHiddenIndices: false, }); expect(IndicesLogic.values).toEqual({ + ...DEFAULT_VALUES, data: { indices: [], isInitialRequest: false, meta, + returnHiddenIndices: false, }, hasNoIndices: false, indices: [], isFirstRequest: false, isLoading: false, meta, + searchParams: { + ...DEFAULT_VALUES.searchParams, + meta, + }, status: Status.SUCCESS, }); }); @@ -172,6 +267,21 @@ describe('IndicesLogic', () => { expect(mockFlashMessageHelpers.flashAPIErrors).toHaveBeenCalledTimes(1); expect(mockFlashMessageHelpers.flashAPIErrors).toHaveBeenCalledWith({}); }); + it('calls flashAPIErrors on deleteError', () => { + IndicesLogic.actions.deleteError({} as HttpError); + expect(mockFlashMessageHelpers.flashAPIErrors).toHaveBeenCalledTimes(1); + expect(mockFlashMessageHelpers.flashAPIErrors).toHaveBeenCalledWith({}); + }); + it('calls flashSuccessToast, closeDeleteModal and fetchIndices on deleteSuccess', () => { + IndicesLogic.actions.fetchIndices = jest.fn(); + IndicesLogic.actions.closeDeleteModal = jest.fn(); + IndicesLogic.actions.deleteSuccess(); + expect(mockFlashMessageHelpers.flashSuccessToast).toHaveBeenCalledTimes(1); + expect(IndicesLogic.actions.fetchIndices).toHaveBeenCalledWith( + IndicesLogic.values.searchParams + ); + expect(IndicesLogic.actions.closeDeleteModal).toHaveBeenCalled(); + }); it('calls makeRequest on fetchIndices', async () => { jest.useFakeTimers(); IndicesLogic.actions.makeRequest = jest.fn(); @@ -223,13 +333,16 @@ describe('IndicesLogic', () => { indices: elasticsearchViewIndices, isInitialRequest: true, meta: DEFAULT_META, + returnHiddenIndices: false, }); expect(IndicesLogic.values).toEqual({ + ...DEFAULT_VALUES, data: { indices: elasticsearchViewIndices, isInitialRequest: true, meta: DEFAULT_META, + returnHiddenIndices: false, }, hasNoIndices: false, indices: elasticsearchViewIndices, @@ -256,9 +369,11 @@ describe('IndicesLogic', () => { ], isInitialRequest: true, meta: DEFAULT_META, + returnHiddenIndices: false, }); expect(IndicesLogic.values).toEqual({ + ...DEFAULT_VALUES, data: { indices: [ { @@ -272,6 +387,7 @@ describe('IndicesLogic', () => { ], isInitialRequest: true, meta: DEFAULT_META, + returnHiddenIndices: false, }, hasNoIndices: false, indices: [ @@ -302,9 +418,11 @@ describe('IndicesLogic', () => { ], isInitialRequest: true, meta: DEFAULT_META, + returnHiddenIndices: false, }); expect(IndicesLogic.values).toEqual({ + ...DEFAULT_VALUES, data: { indices: [ { @@ -314,6 +432,7 @@ describe('IndicesLogic', () => { ], isInitialRequest: true, meta: DEFAULT_META, + returnHiddenIndices: false, }, hasNoIndices: false, indices: [ @@ -343,9 +462,11 @@ describe('IndicesLogic', () => { ], isInitialRequest: true, meta: DEFAULT_META, + returnHiddenIndices: false, }); expect(IndicesLogic.values).toEqual({ + ...DEFAULT_VALUES, data: { indices: [ { @@ -355,6 +476,7 @@ describe('IndicesLogic', () => { ], isInitialRequest: true, meta: DEFAULT_META, + returnHiddenIndices: false, }, hasNoIndices: false, indices: [ @@ -385,9 +507,11 @@ describe('IndicesLogic', () => { ], isInitialRequest: true, meta: DEFAULT_META, + returnHiddenIndices: false, }); expect(IndicesLogic.values).toEqual({ + ...DEFAULT_VALUES, data: { indices: [ { @@ -401,6 +525,7 @@ describe('IndicesLogic', () => { ], isInitialRequest: true, meta: DEFAULT_META, + returnHiddenIndices: false, }, hasNoIndices: false, indices: [ diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_logic.ts index 59640a948ddbc..5b90b26dba002 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_logic.ts @@ -7,15 +7,26 @@ import { kea, MakeLogicType } from 'kea'; +import { i18n } from '@kbn/i18n'; + import { Meta } from '../../../../../common/types'; import { HttpError, Status } from '../../../../../common/types/api'; import { ElasticsearchIndexWithIngestion } from '../../../../../common/types/indices'; +import { Actions } from '../../../shared/api_logic/create_api_logic'; import { DEFAULT_META } from '../../../shared/constants'; -import { flashAPIErrors, clearFlashMessages } from '../../../shared/flash_messages'; +import { + flashAPIErrors, + clearFlashMessages, + flashSuccessToast, +} from '../../../shared/flash_messages'; import { updateMetaPageIndex } from '../../../shared/table_pagination'; +import { + DeleteIndexApiLogic, + DeleteIndexApiLogicArgs, +} from '../../api/index/delete_index_api_logic'; import { FetchIndicesAPILogic } from '../../api/index/fetch_indices_api_logic'; -import { ElasticsearchViewIndex } from '../../types'; -import { indexToViewIndex } from '../../utils/indices'; +import { ElasticsearchViewIndex, IngestionMethod } from '../../types'; +import { getIngestionMethod, indexToViewIndex } from '../../utils/indices'; export interface IndicesActions { apiError(error: HttpError): HttpError; @@ -23,15 +34,25 @@ export interface IndicesActions { indices, isInitialRequest, meta, + returnHiddenIndices, + searchQuery, }: { indices: ElasticsearchIndexWithIngestion[]; isInitialRequest: boolean; meta: Meta; + returnHiddenIndices: boolean; + searchQuery?: string; }): { indices: ElasticsearchIndexWithIngestion[]; isInitialRequest: boolean; meta: Meta; + returnHiddenIndices: boolean; + searchQuery?: string; }; + closeDeleteModal(): void; + deleteError: Actions['apiError']; + deleteIndex: Actions['makeRequest']; + deleteSuccess: Actions['apiSuccess']; fetchIndices({ meta, returnHiddenIndices, @@ -43,34 +64,61 @@ export interface IndicesActions { }): { meta: Meta; returnHiddenIndices: boolean; searchQuery?: string }; makeRequest: typeof FetchIndicesAPILogic.actions.makeRequest; onPaginate(newPageIndex: number): { newPageIndex: number }; - setIsFirstRequest(): boolean; + openDeleteModal(index: ElasticsearchViewIndex): { index: ElasticsearchViewIndex }; + setIsFirstRequest(): void; } export interface IndicesValues { data: typeof FetchIndicesAPILogic.values.data; + deleteModalIndex: ElasticsearchViewIndex | null; + deleteModalIndexName: string; + deleteModalIngestionMethod: IngestionMethod; hasNoIndices: boolean; indices: ElasticsearchViewIndex[]; + isDeleteModalVisible: boolean; isFirstRequest: boolean; isLoading: boolean; meta: Meta; + searchParams: { meta: Meta; returnHiddenIndices: boolean; searchQuery?: string }; status: typeof FetchIndicesAPILogic.values.status; } export const IndicesLogic = kea>({ actions: { + closeDeleteModal: true, fetchIndices: ({ meta, returnHiddenIndices, searchQuery }) => ({ meta, returnHiddenIndices, searchQuery, }), onPaginate: (newPageIndex) => ({ newPageIndex }), - setIsFirstRequest: () => true, + openDeleteModal: (index) => ({ index }), + setIsFirstRequest: true, }, connect: { - actions: [FetchIndicesAPILogic, ['makeRequest', 'apiSuccess', 'apiError']], + actions: [ + FetchIndicesAPILogic, + ['makeRequest', 'apiSuccess', 'apiError'], + DeleteIndexApiLogic, + ['apiError as deleteError', 'apiSuccess as deleteSuccess', 'makeRequest as deleteIndex'], + ], values: [FetchIndicesAPILogic, ['data', 'status']], }, - listeners: ({ actions }) => ({ + listeners: ({ actions, values }) => ({ apiError: (e) => flashAPIErrors(e), + deleteError: (e) => flashAPIErrors(e), + deleteSuccess: () => { + flashSuccessToast( + i18n.translate('xpack.enterpriseSearch.content.indices.deleteIndex.successToast.title', { + defaultMessage: + 'Your index {indexName} and any associated ingestion configurations were successfully deleted', + values: { + indexName: values.deleteModalIndexName, + }, + }) + ); + actions.closeDeleteModal(); + actions.fetchIndices(values.searchParams); + }, fetchIndices: async (input, breakpoint) => { await breakpoint(150); actions.makeRequest(input); @@ -79,6 +127,20 @@ export const IndicesLogic = kea>({ }), path: ['enterprise_search', 'content', 'indices_logic'], reducers: () => ({ + deleteModalIndex: [ + null, + { + closeDeleteModal: () => null, + openDeleteModal: (_, { index }) => index, + }, + ], + isDeleteModalVisible: [ + false, + { + closeDeleteModal: () => false, + openDeleteModal: () => true, + }, + ], isFirstRequest: [ true, { @@ -87,15 +149,28 @@ export const IndicesLogic = kea>({ setIsFirstRequest: () => true, }, ], - meta: [ - DEFAULT_META, + searchParams: [ + { meta: DEFAULT_META, returnHiddenIndices: false }, { - apiSuccess: (_, { meta }) => meta, - onPaginate: (state, { newPageIndex }) => updateMetaPageIndex(state, newPageIndex), + apiSuccess: (_, { meta, returnHiddenIndices, searchQuery }) => ({ + meta, + returnHiddenIndices, + searchQuery, + }), + onPaginate: (state, { newPageIndex }) => ({ + ...state, + meta: updateMetaPageIndex(state.meta, newPageIndex), + }), }, ], }), selectors: ({ selectors }) => ({ + deleteModalIndexName: [() => [selectors.deleteModalIndex], (index) => index?.name ?? ''], + deleteModalIngestionMethod: [ + () => [selectors.deleteModalIndex], + (index: ElasticsearchViewIndex | null) => + index ? getIngestionMethod(index) : IngestionMethod.API, + ], hasNoIndices: [ // We need this to show the landing page on the overview page if there are no indices // We can't rely just on there being no indices, because user might have entered a search query @@ -110,5 +185,6 @@ export const IndicesLogic = kea>({ () => [selectors.status, selectors.isFirstRequest], (status, isFirstRequest) => [Status.LOADING, Status.IDLE].includes(status) && isFirstRequest, ], + meta: [() => [selectors.searchParams], (searchParams) => searchParams.meta], }), }); 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 d68ce14b1b183..8ff7550b61ffd 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 @@ -7,6 +7,8 @@ import React from 'react'; +import { useValues } from 'kea'; + import { CriteriaWithPagination, EuiBasicTable, @@ -18,7 +20,8 @@ import { i18n } from '@kbn/i18n'; import { Meta } from '../../../../../common/types'; import { generateEncodedPath } from '../../../shared/encode_path_params'; -import { EuiLinkTo, EuiButtonIconTo } from '../../../shared/react_router_helpers'; +import { KibanaLogic } from '../../../shared/kibana'; +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'; @@ -37,122 +40,12 @@ const healthColorsMap = { yellow: 'warning', }; -const columns: Array> = [ - { - field: 'name', - name: i18n.translate('xpack.enterpriseSearch.content.searchIndices.name.columnTitle', { - defaultMessage: 'Index name', - }), - render: (name: string) => ( - - {name} - - ), - sortable: true, - truncateText: true, - width: '40%', - }, - { - field: 'health', - name: i18n.translate('xpack.enterpriseSearch.content.searchIndices.health.columnTitle', { - defaultMessage: 'Index health', - }), - render: (health: 'red' | 'green' | 'yellow' | 'unavailable') => ( - - -  {health ?? '-'} - - ), - sortable: true, - truncateText: true, - width: '10%', - }, - { - field: 'count', - name: i18n.translate('xpack.enterpriseSearch.content.searchIndices.docsCount.columnTitle', { - defaultMessage: 'Docs count', - }), - sortable: true, - truncateText: true, - width: '10%', - }, - { - field: 'ingestionMethod', - name: i18n.translate( - 'xpack.enterpriseSearch.content.searchIndices.ingestionMethod.columnTitle', - { - defaultMessage: 'Ingestion method', - } - ), - render: (ingestionMethod: IngestionMethod) => ( - {ingestionMethodToText(ingestionMethod)} - ), - truncateText: true, - width: '10%', - }, - { - name: i18n.translate( - 'xpack.enterpriseSearch.content.searchIndices.ingestionStatus.columnTitle', - { - defaultMessage: 'Ingestion status', - } - ), - render: (index: ElasticsearchViewIndex) => { - const overviewPath = generateEncodedPath(SEARCH_INDEX_PATH, { indexName: index.name }); - if (isCrawlerIndex(index)) { - const label = crawlerStatusToText(index.crawler?.most_recent_crawl_request_status); - - return ( - - ); - } else { - const label = ingestionStatusToText(index.ingestionStatus); - return ( - - ); - } - }, - truncateText: true, - width: '10%', - }, - { - actions: [ - { - render: ({ name }) => ( - - ), - }, - ], - name: i18n.translate('xpack.enterpriseSearch.content.searchIndices.actions.columnTitle', { - defaultMessage: 'Actions', - }), - width: '5%', - }, -]; - interface IndicesTableProps { indices: ElasticsearchViewIndex[]; isLoading?: boolean; meta: Meta; onChange: (criteria: CriteriaWithPagination) => void; + onDelete: (index: ElasticsearchViewIndex) => void; } export const IndicesTable: React.FC = ({ @@ -160,13 +53,138 @@ export const IndicesTable: React.FC = ({ isLoading, meta, onChange, -}) => ( - -); + onDelete, +}) => { + const { navigateToUrl } = useValues(KibanaLogic); + const columns: Array> = [ + { + field: 'name', + name: i18n.translate('xpack.enterpriseSearch.content.searchIndices.name.columnTitle', { + defaultMessage: 'Index name', + }), + render: (name: string) => ( + + {name} + + ), + sortable: true, + truncateText: true, + width: '40%', + }, + { + field: 'health', + name: i18n.translate('xpack.enterpriseSearch.content.searchIndices.health.columnTitle', { + defaultMessage: 'Index health', + }), + render: (health: 'red' | 'green' | 'yellow' | 'unavailable') => ( + + +  {health ?? '-'} + + ), + sortable: true, + truncateText: true, + width: '10%', + }, + { + field: 'count', + name: i18n.translate('xpack.enterpriseSearch.content.searchIndices.docsCount.columnTitle', { + defaultMessage: 'Docs count', + }), + sortable: true, + truncateText: true, + width: '10%', + }, + { + field: 'ingestionMethod', + name: i18n.translate( + 'xpack.enterpriseSearch.content.searchIndices.ingestionMethod.columnTitle', + { + defaultMessage: 'Ingestion method', + } + ), + render: (ingestionMethod: IngestionMethod) => ( + {ingestionMethodToText(ingestionMethod)} + ), + truncateText: true, + width: '10%', + }, + { + name: i18n.translate( + 'xpack.enterpriseSearch.content.searchIndices.ingestionStatus.columnTitle', + { + defaultMessage: 'Ingestion status', + } + ), + render: (index: ElasticsearchViewIndex) => { + const overviewPath = generateEncodedPath(SEARCH_INDEX_PATH, { indexName: index.name }); + if (isCrawlerIndex(index)) { + const label = crawlerStatusToText(index.crawler?.most_recent_crawl_request_status); + + return ( + + ); + } else { + const label = ingestionStatusToText(index.ingestionStatus); + return ( + + ); + } + }, + truncateText: true, + width: '15%', + }, + { + actions: [ + { + description: 'View this index', + icon: 'eye', + isPrimary: false, + name: (index) => `View ${index.name}`, + onClick: (index) => + navigateToUrl( + generateEncodedPath(SEARCH_INDEX_PATH, { + indexName: index.name, + }) + ), + type: 'icon', + }, + { + available: (index) => !isCrawlerIndex(index), + color: 'danger', + description: 'Delete this index', + icon: 'trash', + isPrimary: false, + name: (index) => `Delete ${index.name}`, + onClick: (index) => onDelete(index), + type: 'icon', + }, + ], + 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 96019c8139c97..f2efea5ef1e51 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 @@ -34,22 +34,20 @@ import { useLocalStorage } from '../../../shared/use_local_storage'; import { NEW_INDEX_PATH } from '../../routes'; import { EnterpriseSearchContentPageTemplate } from '../layout/page_template'; +import { DeleteIndexModal } from './delete_index_modal'; import { IndicesLogic } from './indices_logic'; import { IndicesTable } from './indices_table'; import './search_indices.scss'; export const baseBreadcrumbs = [ - i18n.translate('xpack.enterpriseSearch.content.searchIndices.content.breadcrumb', { - defaultMessage: 'Content', - }), i18n.translate('xpack.enterpriseSearch.content.searchIndices.searchIndices.breadcrumb', { defaultMessage: 'Elasticsearch indices', }), ]; export const SearchIndices: React.FC = () => { - const { fetchIndices, onPaginate, setIsFirstRequest } = useActions(IndicesLogic); + const { fetchIndices, onPaginate, openDeleteModal, setIsFirstRequest } = useActions(IndicesLogic); const { meta, indices, hasNoIndices, isLoading } = useValues(IndicesLogic); const [showHiddenIndices, setShowHiddenIndices] = useState(false); const [searchQuery, setSearchValue] = useState(''); @@ -85,6 +83,7 @@ export const SearchIndices: React.FC = () => { return ( <> + { - + ) : ( diff --git a/x-pack/plugins/enterprise_search/public/applications/index.test.tsx b/x-pack/plugins/enterprise_search/public/applications/index.test.tsx index c8722adfebbd2..455707779e739 100644 --- a/x-pack/plugins/enterprise_search/public/applications/index.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/index.test.tsx @@ -7,6 +7,7 @@ import React from 'react'; +import { act } from '@testing-library/react'; import { getContext } from 'kea'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; @@ -47,7 +48,7 @@ describe('renderApp', () => { const unmount = renderApp(MockApp, kibanaDeps, pluginData); expect(mockContainer.querySelector('.hello-world')).not.toBeNull(); - unmount(); + act(() => unmount()); expect(mockContainer.innerHTML).toEqual(''); }); @@ -60,20 +61,30 @@ describe('renderApp', () => { }; describe('Enterprise Search apps', () => { - afterEach(() => unmount()); + afterEach(() => { + act(() => { + unmount(); + }); + }); it('renders EnterpriseSearchOverview', () => { - mount(EnterpriseSearchOverview); + act(() => { + mount(EnterpriseSearchOverview); + }); expect(mockContainer.querySelector('.kbnPageTemplate')).not.toBeNull(); }); it('renders AppSearch', () => { - mount(AppSearch); + act(() => { + mount(AppSearch); + }); expect(mockContainer.querySelector('.setupGuide')).not.toBeNull(); }); it('renders WorkplaceSearch', () => { - mount(WorkplaceSearch); + act(() => { + mount(WorkplaceSearch); + }); expect(mockContainer.querySelector('.setupGuide')).not.toBeNull(); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts index 516fa3d2ef392..5f985aeae169b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts @@ -13,6 +13,7 @@ import { ENTERPRISE_SEARCH_OVERVIEW_PLUGIN, APP_SEARCH_PLUGIN, WORKPLACE_SEARCH_PLUGIN, + ENTERPRISE_SEARCH_CONTENT_PLUGIN, } from '../../../../common/constants'; import { stripLeadingSlash } from '../../../../common/strip_slashes'; @@ -118,3 +119,9 @@ export const useWorkplaceSearchBreadcrumbs = (breadcrumbs: Breadcrumbs = []) => { text: WORKPLACE_SEARCH_PLUGIN.NAME, path: '/' }, ...breadcrumbs, ]); + +export const useEnterpriseSearchContentBreadcrumbs = (breadcrumbs: Breadcrumbs = []) => + useEnterpriseSearchBreadcrumbs([ + { text: ENTERPRISE_SEARCH_CONTENT_PLUGIN.NAV_TITLE, path: '/' }, + ...breadcrumbs, + ]); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/index.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/index.ts index b14171b4798a5..d410d392f92a8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/index.ts @@ -7,6 +7,7 @@ export { SetEnterpriseSearchChrome, + SetEnterpriseSearchContentChrome, SetElasticsearchChrome, SetAppSearchChrome, SetWorkplaceSearchChrome, diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx index 3698a8cac35a9..83d05bb2bef73 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx @@ -14,6 +14,7 @@ import { KibanaLogic } from '../kibana'; import { useGenerateBreadcrumbs, useEnterpriseSearchBreadcrumbs, + useEnterpriseSearchContentBreadcrumbs, useElasticsearchBreadcrumbs, useAppSearchBreadcrumbs, useWorkplaceSearchBreadcrumbs, @@ -113,5 +114,22 @@ export const SetWorkplaceSearchChrome: React.FC = ({ trail = [] return null; }; +export const SetEnterpriseSearchContentChrome: React.FC = ({ trail = [] }) => { + const { setBreadcrumbs, setDocTitle } = useValues(KibanaLogic); + + const title = reverseArray(trail); + const docTitle = appSearchTitle(title); + + const crumbs = useGenerateBreadcrumbs(trail); + const breadcrumbs = useEnterpriseSearchContentBreadcrumbs(crumbs); + + useEffect(() => { + setBreadcrumbs(breadcrumbs); + setDocTitle(docTitle); + }, [trail]); + + return null; +}; + // Small util - performantly reverses an array without mutating the original array const reverseArray = (array: string[]) => array.slice().reverse(); diff --git a/x-pack/plugins/enterprise_search/server/lib/analytics/fetch_analytics_collection.test.ts b/x-pack/plugins/enterprise_search/server/lib/analytics/fetch_analytics_collection.test.ts index e157df1df16f6..6fb4e55dd6361 100644 --- a/x-pack/plugins/enterprise_search/server/lib/analytics/fetch_analytics_collection.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/analytics/fetch_analytics_collection.test.ts @@ -9,7 +9,10 @@ import { IScopedClusterClient } from '@kbn/core-elasticsearch-server'; import { ANALYTICS_COLLECTIONS_INDEX } from '../..'; -import { fetchAnalyticsCollectionByName } from './fetch_analytics_collection'; +import { + fetchAnalyticsCollectionByName, + fetchAnalyticsCollections, +} from './fetch_analytics_collection'; import { setupAnalyticsCollectionIndex } from './setup_indices'; jest.mock('./setup_indices', () => ({ @@ -28,6 +31,75 @@ describe('fetch analytics collection lib function', () => { jest.clearAllMocks(); }); + describe('fetch collections', () => { + it('should return a list of analytics collections', async () => { + mockClient.asCurrentUser.search.mockImplementationOnce(() => + Promise.resolve({ + hits: { + hits: [ + { _id: '2', _source: { name: 'example' } }, + { _id: '1', _source: { name: 'example2' } }, + ], + }, + }) + ); + await expect( + fetchAnalyticsCollections(mockClient as unknown as IScopedClusterClient) + ).resolves.toEqual([ + { id: '2', name: 'example' }, + { id: '1', name: 'example2' }, + ]); + }); + + it('should setup the indexes if none exist and return an empty array', async () => { + mockClient.asCurrentUser.search.mockImplementationOnce(() => + Promise.reject({ + meta: { + body: { + error: { + type: 'index_not_found_exception', + }, + }, + }, + }) + ); + + await expect( + fetchAnalyticsCollections(mockClient as unknown as IScopedClusterClient) + ).resolves.toEqual([]); + + expect(setupAnalyticsCollectionIndex as jest.Mock).toHaveBeenCalledWith( + mockClient.asCurrentUser + ); + }); + + it('should not call setup analytics index on other errors and return error', async () => { + const error = { + meta: { + body: { + error: { + type: 'other error', + }, + }, + }, + }; + mockClient.asCurrentUser.search.mockImplementationOnce(() => Promise.reject(error)); + await expect( + fetchAnalyticsCollections(mockClient as unknown as IScopedClusterClient) + ).rejects.toMatchObject(error); + + expect(mockClient.asCurrentUser.search).toHaveBeenCalledWith({ + from: 0, + index: ANALYTICS_COLLECTIONS_INDEX, + query: { + match_all: {}, + }, + size: 1000, + }); + expect(setupAnalyticsCollectionIndex as jest.Mock).not.toHaveBeenCalled(); + }); + }); + describe('fetch collection by name', () => { it('should fetch analytics collection by name', async () => { mockClient.asCurrentUser.search.mockImplementationOnce(() => diff --git a/x-pack/plugins/enterprise_search/server/lib/analytics/fetch_analytics_collection.ts b/x-pack/plugins/enterprise_search/server/lib/analytics/fetch_analytics_collection.ts index 46f718d23976d..ef356ae2bca2c 100644 --- a/x-pack/plugins/enterprise_search/server/lib/analytics/fetch_analytics_collection.ts +++ b/x-pack/plugins/enterprise_search/server/lib/analytics/fetch_analytics_collection.ts @@ -5,12 +5,14 @@ * 2.0. */ +import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { IScopedClusterClient } from '@kbn/core-elasticsearch-server'; import { ANALYTICS_COLLECTIONS_INDEX } from '../..'; import { AnalyticsCollection } from '../../../common/types/analytics'; import { isIndexNotFoundException } from '../../utils/identify_exceptions'; +import { fetchAll } from '../fetch_all'; import { setupAnalyticsCollectionIndex } from './setup_indices'; @@ -36,3 +38,19 @@ export const fetchAnalyticsCollectionByName = async ( return undefined; } }; + +export const fetchAnalyticsCollections = async ( + client: IScopedClusterClient +): Promise => { + const query: QueryDslQueryContainer = { match_all: {} }; + + try { + return await fetchAll(client, ANALYTICS_COLLECTIONS_INDEX, query); + } catch (error) { + if (isIndexNotFoundException(error)) { + await setupAnalyticsCollectionIndex(client.asCurrentUser); + return []; + } + throw error; + } +}; diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/analytics.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/analytics.ts index be53b797239a1..7035329e4d314 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/analytics.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/analytics.ts @@ -11,11 +11,24 @@ import { i18n } from '@kbn/i18n'; import { ErrorCode } from '../../../common/types/error_codes'; import { addAnalyticsCollection } from '../../lib/analytics/add_analytics_collection'; +import { fetchAnalyticsCollections } from '../../lib/analytics/fetch_analytics_collection'; import { RouteDependencies } from '../../plugin'; import { createError } from '../../utils/create_error'; import { elasticsearchErrorHandler } from '../../utils/elasticsearch_error_handler'; export function registerAnalyticsRoutes({ router, log }: RouteDependencies) { + router.get( + { + path: '/internal/enterprise_search/analytics/collections', + validate: {}, + }, + elasticsearchErrorHandler(log, async (context, request, response) => { + const { client } = (await context.core).elasticsearch; + const collections = await fetchAnalyticsCollections(client); + return response.ok({ body: collections }); + }) + ); + router.post( { path: '/internal/enterprise_search/analytics/collections', diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.ts index 5d5f72c297727..6741721053b86 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.ts @@ -9,6 +9,7 @@ import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; import { ErrorCode } from '../../../common/types/error_codes'; +import { deleteConnectorById } from '../../lib/connectors/delete_connector'; import { fetchConnectorByIndexName, fetchConnectors } from '../../lib/connectors/fetch_connectors'; import { fetchCrawlerByIndexName, fetchCrawlers } from '../../lib/crawler/fetch_crawlers'; @@ -124,6 +125,52 @@ export function registerIndexRoutes({ router, log }: RouteDependencies) { }) ); + router.delete( + { + path: '/internal/enterprise_search/indices/{indexName}', + validate: { + params: schema.object({ + indexName: schema.string(), + }), + }, + }, + elasticsearchErrorHandler(log, async (context, request, response) => { + const indexName = decodeURIComponent(request.params.indexName); + const { client } = (await context.core).elasticsearch; + + try { + const connector = await fetchConnectorByIndexName(client, indexName); + const crawler = await fetchCrawlerByIndexName(client, indexName); + + if (connector) { + await deleteConnectorById(client, connector.id); + } + + if (crawler) { + // do nothing for now because we don't have a way to delete a crawler yet + } + + await client.asCurrentUser.indices.delete({ index: indexName }); + + return response.ok({ + body: {}, + headers: { 'content-type': 'application/json' }, + }); + } catch (error) { + if (isIndexNotFoundException(error)) { + return createError({ + errorCode: ErrorCode.INDEX_NOT_FOUND, + message: 'Could not find index', + response, + statusCode: 404, + }); + } + + throw error; + } + }) + ); + router.get( { path: '/internal/enterprise_search/indices/{indexName}/exists', diff --git a/x-pack/plugins/files/common/api_routes.ts b/x-pack/plugins/files/common/api_routes.ts index f3647edaf83f0..9eb6671465799 100644 --- a/x-pack/plugins/files/common/api_routes.ts +++ b/x-pack/plugins/files/common/api_routes.ts @@ -99,7 +99,7 @@ export type UpdateFileKindHttpEndpoint = HttpApiInterfaceEntryDefinition< export type UploadFileKindHttpEndpoint = HttpApiInterfaceEntryDefinition< { id: string }, unknown, - any, + { body: unknown }, { ok: true; size: number; diff --git a/x-pack/plugins/files/common/types.ts b/x-pack/plugins/files/common/types.ts index d3c8795fc7321..4c8e03497fa3b 100644 --- a/x-pack/plugins/files/common/types.ts +++ b/x-pack/plugins/files/common/types.ts @@ -440,6 +440,8 @@ export interface FileKind { id: string; /** * Maximum size, in bytes, a file of this kind can be. + * + * @default 4MiB */ maxSizeBytes?: number; diff --git a/x-pack/plugins/files/public/files_client/files_client.ts b/x-pack/plugins/files/public/files_client/files_client.ts index 3abd6c935541c..2f01405ee1492 100644 --- a/x-pack/plugins/files/public/files_client/files_client.ts +++ b/x-pack/plugins/files/public/files_client/files_client.ts @@ -77,7 +77,7 @@ const commonBodyHeaders = { }; export const createFilesClient = ({ http, fileKind }: Args): FilesClient => { - return { + const api: FilesClient = { create: (args) => { return http.post(apiRoutes.getCreateFileRoute(fileKind), { headers: commonBodyHeaders, @@ -88,13 +88,15 @@ export const createFilesClient = ({ http, fileKind }: Args): FilesClient => { return http.delete(apiRoutes.getDeleteRoute(fileKind, args.id)); }, download: (args) => { - return http.get(apiRoutes.getDownloadRoute(fileKind, args.id, args.fileName)); + return http.get(apiRoutes.getDownloadRoute(fileKind, args.id, args.fileName), { + headers: { Accept: '*/*' }, + }); }, getById: (args) => { return http.get(apiRoutes.getByIdRoute(fileKind, args.id)); }, - list: ({ page, perPage }) => { - return http.get(apiRoutes.getListRoute(fileKind, page, perPage)); + list(args = {}) { + return http.get(apiRoutes.getListRoute(fileKind, args.page, args.perPage)); }, update: ({ id, ...body }) => { return http.patch(apiRoutes.getUpdateRoute(fileKind, id), { @@ -105,9 +107,10 @@ export const createFilesClient = ({ http, fileKind }: Args): FilesClient => { upload: (args) => { return http.put(apiRoutes.getUploadRoute(fileKind, args.id), { headers: { - 'content-type': 'application/octet-stream', + 'Content-Type': 'application/octet-stream', }, - body: args.body, + + body: args.body as BodyInit, }); }, share: ({ fileId, name, validUntil }) => { @@ -137,5 +140,9 @@ export const createFilesClient = ({ http, fileKind }: Args): FilesClient => { publicDownload: ({ token, fileName }) => { return http.get(apiRoutes.getPublicDownloadRoute(token, fileName)); }, + getDownloadHref: ({ id }) => { + return `${http.basePath.prepend(apiRoutes.getDownloadRoute(fileKind, id))}`; + }, }; + return api; }; diff --git a/x-pack/plugins/files/public/index.ts b/x-pack/plugins/files/public/index.ts index 4b707aaa3b6a2..36a570c209683 100644 --- a/x-pack/plugins/files/public/index.ts +++ b/x-pack/plugins/files/public/index.ts @@ -6,8 +6,8 @@ */ import { FilesPlugin } from './plugin'; - -export type { FilesClient, FilesClientFactory } from './types'; +export type { FilesSetup, FilesStart } from './plugin'; +export type { FilesClient, FilesClientFactory, FilesClientResponses } from './types'; export function plugin() { return new FilesPlugin(); diff --git a/x-pack/plugins/files/public/types.ts b/x-pack/plugins/files/public/types.ts index 24a3125213020..3a52b863bc7e4 100644 --- a/x-pack/plugins/files/public/types.ts +++ b/x-pack/plugins/files/public/types.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { FileJSON } from '../common'; import type { FindFilesHttpEndpoint, FileShareHttpEndpoint, @@ -30,6 +31,10 @@ type ClientMethodFrom = ( args: E['inputs']['body'] & E['inputs']['params'] & E['inputs']['query'] ) => Promise; +type ClientMethodOptionalArgsFrom = ( + args?: E['inputs']['body'] & E['inputs']['params'] & E['inputs']['query'] +) => Promise; + /** * A client that can be used to manage a specific {@link FileKind}. */ @@ -57,7 +62,7 @@ export interface FilesClient { * * @param args - list files args */ - list: ClientMethodFrom; + list: ClientMethodOptionalArgsFrom; /** * Find a set of files given some filters. * @@ -123,8 +128,18 @@ export interface FilesClient { * @param args - Get public download arguments. */ publicDownload: ClientMethodFrom; + + /** + * Get a string for downloading a file that can be passed to a button element's + * href for download. + */ + getDownloadHref: (file: FileJSON) => string; } +export type FilesClientResponses = { + [K in keyof FilesClient]: Awaited>; +}; + /** * A factory for creating a {@link FilesClient} */ diff --git a/x-pack/plugins/files/server/routes/file_kind/upload.ts b/x-pack/plugins/files/server/routes/file_kind/upload.ts index dc6e8e7f4b268..c563cddad4877 100644 --- a/x-pack/plugins/files/server/routes/file_kind/upload.ts +++ b/x-pack/plugins/files/server/routes/file_kind/upload.ts @@ -54,6 +54,8 @@ export const handler: FileKindsRequestHandler = async ( return res.ok({ body }); }; +const fourMiB = 4 * 1024 * 1024; + export function register(fileKindRouter: FileKindRouter, fileKind: FileKind) { if (fileKind.http.create) { fileKindRouter[method]( @@ -69,6 +71,7 @@ export function register(fileKindRouter: FileKindRouter, fileKind: FileKind) { output: 'stream', parse: false, accepts: fileKind.allowedMimeTypes ?? 'application/octet-stream', + maxBytes: fileKind.maxSizeBytes ?? fourMiB, }, }, }, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx index 77734feb67e21..1662f6d0c7214 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx @@ -132,7 +132,7 @@ export const PackagePolicyInputStreamConfig: React.FunctionComponent<{ - + {packageInputStream.description} ) : null} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx index 327037a6f2745..812d5fc2233de 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx @@ -155,7 +155,7 @@ export const PackagePolicyInputVarField: React.FunctionComponent<{ ) : null } - helpText={} + helpText={description && } > {field} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/agent_policy_debugger.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/agent_policy_debugger.tsx index 71ee096f1e4fa..78be94ef07048 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/agent_policy_debugger.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/agent_policy_debugger.tsx @@ -16,7 +16,7 @@ import { EuiSpacer, EuiText, } from '@elastic/eui'; -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; @@ -52,7 +52,7 @@ export const AgentPolicyDebugger: React.FunctionComponent = () => { // TODO: Depending on the number of agent policies, this might need to be switched to // `useInfinite` query with an infinite scrolling approach in the dropdown options. - const { data, status } = useQuery('debug-agent-policies', fetchAgentPolicies); + const { data, status } = useQuery(['debug-agent-policies'], fetchAgentPolicies); const agentPolicies = data?.data?.items ?? []; const comboBoxOptions = agentPolicies.map((policy) => ({ @@ -68,7 +68,7 @@ export const AgentPolicyDebugger: React.FunctionComponent = () => { const onDelete = () => { setSelectedPolicyId(undefined); - queryClient.invalidateQueries('debug-agent-policies'); + queryClient.invalidateQueries(['debug-agent-policies']); }; if (status === 'error') { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/fleet_index_debugger.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/fleet_index_debugger.tsx index 7c0c81858414a..3f678d4463480 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/fleet_index_debugger.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/fleet_index_debugger.tsx @@ -15,7 +15,7 @@ import { EuiSpacer, EuiText, } from '@elastic/eui'; -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/integration_debugger.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/integration_debugger.tsx index 7140a582980cf..9c3fa21c752f8 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/integration_debugger.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/integration_debugger.tsx @@ -20,7 +20,7 @@ import { EuiSpacer, EuiText, } from '@elastic/eui'; -import { useMutation, useQuery } from 'react-query'; +import { useMutation, useQuery } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -58,7 +58,7 @@ export const IntegrationDebugger: React.FunctionComponent = () => { const [isReinstallModalVisible, setIsReinstallModalVisible] = useState(false); const [isUninstallModalVisible, setIsUninstallModalVisible] = useState(false); - const integrations = useQuery('debug-integrations', fetchInstalledIntegrations); + const integrations = useQuery(['debug-integrations'], fetchInstalledIntegrations); const uninstallMutation = useMutation(async (integration: PackageListItem) => { const response = await sendRemovePackage(integration.name, integration.version, true); @@ -86,7 +86,7 @@ export const IntegrationDebugger: React.FunctionComponent = () => { setSelectedIntegrationId(undefined); setIsUninstallModalVisible(false); - queryClient.invalidateQueries('debug-integrations'); + queryClient.invalidateQueries(['debug-integrations']); return response.data; }); @@ -132,7 +132,7 @@ export const IntegrationDebugger: React.FunctionComponent = () => { setSelectedIntegrationId(undefined); setIsReinstallModalVisible(false); - queryClient.invalidateQueries('debug-integrations'); + queryClient.invalidateQueries(['debug-integrations']); return installResponse.data; }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/orphaned_integration_policy_debugger.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/orphaned_integration_policy_debugger.tsx index abc84ad425900..3febbe36c56f7 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/orphaned_integration_policy_debugger.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/orphaned_integration_policy_debugger.tsx @@ -15,7 +15,7 @@ import { EuiButton, EuiConfirmModal, } from '@elastic/eui'; -import { useMutation, useQuery } from 'react-query'; +import { useMutation, useQuery } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -46,7 +46,7 @@ export const OrphanedIntegrationPolicyDebugger: React.FunctionComponent = () => const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false); const [isDeleteAllModalVisible, setIsDeleteAllModalVisible] = useState(false); - const { data: orphanedPolicies } = useQuery('debug-orphaned-policies', fetchOrphanedPolicies); + const { data: orphanedPolicies } = useQuery(['debug-orphaned-policies'], fetchOrphanedPolicies); const comboBoxOptions = orphanedPolicies?.map((policy: { id: string; name: string }) => ({ @@ -83,7 +83,7 @@ export const OrphanedIntegrationPolicyDebugger: React.FunctionComponent = () => defaultMessage: 'Successfully deleted orphaned policy', }) ); - queryClient.invalidateQueries('debug-orphaned-policies'); + queryClient.invalidateQueries(['debug-orphaned-policies']); setSelectedPolicyId(undefined); setIsDeleteModalVisible(false); @@ -112,7 +112,7 @@ export const OrphanedIntegrationPolicyDebugger: React.FunctionComponent = () => defaultMessage: 'Successfully deleted all orphaned policies', }) ); - queryClient.invalidateQueries('debug-orphaned-policies'); + queryClient.invalidateQueries(['debug-orphaned-policies']); setSelectedPolicyId(undefined); setIsDeleteAllModalVisible(false); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/preconfiguration_debugger.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/preconfiguration_debugger.tsx index 35e9bcba75492..8184242f99e8d 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/preconfiguration_debugger.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/preconfiguration_debugger.tsx @@ -17,7 +17,7 @@ import { EuiLink, EuiConfirmModal, } from '@elastic/eui'; -import { useMutation, useQuery } from 'react-query'; +import { useMutation, useQuery } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -55,7 +55,7 @@ export const PreconfigurationDebugger: React.FunctionComponent = () => { const [isResetAllModalVisible, setIsResetAllModalVisible] = useState(false); const preconfiguredPolicies = useQuery( - 'debug-preconfigured-policies', + ['debug-preconfigured-policies'], fetchPreconfiguredPolicies ); @@ -91,7 +91,7 @@ export const PreconfigurationDebugger: React.FunctionComponent = () => { defaultMessage: 'Successfully reset policy', }) ); - queryClient.invalidateQueries('debug-preconfigured-policies'); + queryClient.invalidateQueries(['debug-preconfigured-policies']); setSelectedPolicyId(undefined); setIsResetModalVisible(false); @@ -116,7 +116,7 @@ export const PreconfigurationDebugger: React.FunctionComponent = () => { defaultMessage: 'Successfully reset policies', }) ); - queryClient.invalidateQueries('debug-preconfigured-policies'); + queryClient.invalidateQueries(['debug-preconfigured-policies']); setSelectedPolicyId(undefined); setIsResetAllModalVisible(false); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/saved_object_debugger.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/saved_object_debugger.tsx index c23b383d96f9d..de95b08a4dfb6 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/saved_object_debugger.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/saved_object_debugger.tsx @@ -6,7 +6,7 @@ */ import React, { useState, useRef } from 'react'; -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { EuiFlexGroup, EuiFlexItem, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/saved_object_names_combo.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/saved_object_names_combo.tsx index 9458bca298b9a..aa9d7cb011e83 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/saved_object_names_combo.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/debug/components/saved_object_names_combo.tsx @@ -6,7 +6,7 @@ */ import React, { forwardRef, useImperativeHandle } from 'react'; -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { EuiComboBox } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/debug/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/debug/index.tsx index b54a97d3b270c..89c61a78eb61c 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/debug/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/debug/index.tsx @@ -18,8 +18,8 @@ import { EuiText, EuiTitle, } from '@elastic/eui'; -import { QueryClient, QueryClientProvider } from 'react-query'; -import { ReactQueryDevtools } from 'react-query/devtools'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; diff --git a/x-pack/plugins/fleet/public/applications/integrations/components/header/header_portal.tsx b/x-pack/plugins/fleet/public/applications/integrations/components/header/header_portal.tsx index 190a6e1ba49c7..8b0671f0a9a45 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/components/header/header_portal.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/components/header/header_portal.tsx @@ -8,7 +8,7 @@ import type { AppMountParameters } from '@kbn/core/public'; import type { FC } from 'react'; import React, { useEffect, useMemo } from 'react'; -import { createPortalNode, InPortal, OutPortal } from 'react-reverse-portal'; +import { createHtmlPortalNode, InPortal, OutPortal } from 'react-reverse-portal'; import { toMountPoint } from '@kbn/kibana-react-plugin/public'; @@ -18,7 +18,7 @@ export interface Props { } export const HeaderPortal: FC = ({ children, setHeaderActionMenu, theme$ }) => { - const portalNode = useMemo(() => createPortalNode(), []); + const portalNode = useMemo(() => createHtmlPortalNode(), []); useEffect(() => { setHeaderActionMenu((element) => { diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/markdown_renderers.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/markdown_renderers.tsx index 2cbdfe3671c4e..8734d051e0be7 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/markdown_renderers.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/markdown_renderers.tsx @@ -11,9 +11,9 @@ import { EuiTableHeaderCell, EuiTableRow, EuiTableRowCell, - EuiText, } from '@elastic/eui'; import React from 'react'; +import type { TransformOptions } from 'react-markdown'; /** prevents links to the new pages from accessing `window.opener` */ const REL_NOOPENER = 'noopener'; @@ -30,42 +30,25 @@ const CODE_LANGUAGE_OVERRIDES: Record = { $yml: 'yml', }; -export const markdownRenderers = { - root: ({ children }: { children: React.ReactNode[] }) => ( - {children} - ), - table: ({ children }: { children: React.ReactNode[] }) => ( - {children}
- ), - tableRow: ({ children }: { children: React.ReactNode[] }) => ( - {children} - ), - tableCell: ({ isHeader, children }: { isHeader: boolean; children: React.ReactNode[] }) => { - return isHeader ? ( - {children} - ) : ( - {children} - ); - }, +export const markdownRenderers: TransformOptions['components'] = { + table: ({ children }) => {children}
, + tr: ({ children }) => {children}, + th: ({ children }) => {children}, + td: ({ children }) => {children}, // the headings used in markdown don't match our page so mapping them to the appropriate one - heading: ({ level, children }: { level: number; children: React.ReactNode[] }) => { - switch (level) { - case 1: - return

{children}

; - case 2: - return

{children}

; - case 3: - return
{children}
; - default: - return
{children}
; - } - }, + h1: ({ children }) =>

{children}

, + h2: ({ children }) =>

{children}

, + h3: ({ children }) =>
{children}
, + h4: ({ children }) =>
{children}
, + h5: ({ children }) =>
{children}
, + h6: ({ children }) =>
{children}
, link: ({ children, href }: { children: React.ReactNode[]; href?: string }) => ( {children} ), - code: ({ language, value }: { language: string; value: string }) => { + // @ts-expect-error update types + code: ({ language, value }) => { let parsedLang = language; // Some integrations export code block content that includes language tags that have since diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/readme.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/readme.tsx index 240b3c7bf7442..f7e9c2a5b4e8f 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/readme.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/readme.tsx @@ -43,11 +43,11 @@ export function Readme({ return ( {markdown !== undefined ? ( - + + + {markdown} + + ) : ( {/* simulates a long page of text loading */} diff --git a/x-pack/plugins/global_search_bar/public/components/search_bar.tsx b/x-pack/plugins/global_search_bar/public/components/search_bar.tsx index c85c6f74b1641..e4d87d26cbecc 100644 --- a/x-pack/plugins/global_search_bar/public/components/search_bar.tsx +++ b/x-pack/plugins/global_search_bar/public/components/search_bar.tsx @@ -40,7 +40,9 @@ import './search_bar.scss'; const isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0; -const blurEvent = new FocusEvent('blur'); +const blurEvent = new FocusEvent('focusout', { + bubbles: true, +}); const sortByScore = (a: GlobalSearchResult, b: GlobalSearchResult): number => { if (a.score < b.score) return 1; diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx index d8c1fe071fe92..30355ea1a9a23 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import React, { useContext } from 'react'; +import React from 'react'; import { transparentize } from 'polished'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; @@ -17,7 +17,7 @@ import { LogEntryColumnWidths, } from './log_entry_column'; import { ASSUMED_SCROLLBAR_WIDTH } from './vertical_scroll_panel'; -import { LogPositionState } from '../../../containers/logs/log_position'; +import { useLogPositionStateContext } from '../../../containers/logs/log_position'; import { localizedDate } from '../../../../common/formatters/datetime'; import { LogColumnRenderConfiguration, @@ -30,7 +30,7 @@ export const LogColumnHeaders: React.FunctionComponent<{ columnConfigurations: LogColumnRenderConfiguration[]; columnWidths: LogEntryColumnWidths; }> = ({ columnConfigurations, columnWidths }) => { - const { firstVisiblePosition } = useContext(LogPositionState.Context); + const { firstVisiblePosition } = useLogPositionStateContext(); return ( {columnConfigurations.map((columnConfiguration) => { diff --git a/x-pack/plugins/infra/public/containers/logs/log_filter/log_filter_state.ts b/x-pack/plugins/infra/public/containers/logs/log_filter/log_filter_state.ts index 55b554560b743..70d72c25158da 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_filter/log_filter_state.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_filter/log_filter_state.ts @@ -105,4 +105,5 @@ export const useLogFilterState = ({ indexPattern }: { indexPattern: DataViewBase }; }; -export const LogFilterState = createContainer(useLogFilterState); +export const [LogFilterStateProvider, useLogFilterStateContext] = + createContainer(useLogFilterState); diff --git a/x-pack/plugins/infra/public/containers/logs/log_filter/with_log_filter_url_state.tsx b/x-pack/plugins/infra/public/containers/logs/log_filter/with_log_filter_url_state.tsx index 5a62cb5e38677..2a5970721f5e5 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_filter/with_log_filter_url_state.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_filter/with_log_filter_url_state.tsx @@ -6,13 +6,13 @@ */ import * as rt from 'io-ts'; -import React, { useContext } from 'react'; +import React from 'react'; import { Query } from '@kbn/es-query'; import { replaceStateKeyInQueryString, UrlStateContainer } from '../../../utils/url_state'; -import { LogFilterState } from './log_filter_state'; +import { useLogFilterStateContext } from './log_filter_state'; export const WithLogFilterUrlState: React.FC = () => { - const { filterQuery, applyLogFilterQuery } = useContext(LogFilterState.Context); + const { filterQuery, applyLogFilterQuery } = useLogFilterStateContext(); return ( { const [highlightTerms, setHighlightTerms] = useState([]); - const { visibleMidpoint, jumpToTargetPosition, startTimestamp, endTimestamp } = useContext( - LogPositionState.Context - ); + const { visibleMidpoint, jumpToTargetPosition, startTimestamp, endTimestamp } = + useLogPositionStateContext(); const throttledStartTimestamp = useThrottle(startTimestamp, FETCH_THROTTLE_INTERVAL); const throttledEndTimestamp = useThrottle(endTimestamp, FETCH_THROTTLE_INTERVAL); @@ -89,4 +88,5 @@ export const useLogHighlightsState = ({ }; }; -export const LogHighlightsState = createContainer(useLogHighlightsState); +export const [LogHighlightsStateProvider, useLogHighlightsStateContext] = + createContainer(useLogHighlightsState); diff --git a/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts b/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts index 59b7d00431459..521a5bf8562fc 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_position/log_position_state.ts @@ -249,4 +249,5 @@ export const useLogPositionState: () => LogPositionStateParams & LogPositionCall return { ...state, ...callbacks }; }; -export const LogPositionState = createContainer(useLogPositionState); +export const [LogPositionStateProvider, useLogPositionStateContext] = + createContainer(useLogPositionState); diff --git a/x-pack/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx b/x-pack/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx index c0da683568f39..d4d8075a2598f 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_position/with_log_position_url_state.tsx @@ -5,11 +5,11 @@ * 2.0. */ -import React, { useContext, useMemo } from 'react'; +import React, { useMemo } from 'react'; import { pickTimeKey } from '../../../../common/time'; import { replaceStateKeyInQueryString, UrlStateContainer } from '../../../utils/url_state'; -import { LogPositionState, LogPositionStateParams } from './log_position_state'; +import { useLogPositionStateContext, LogPositionStateParams } from './log_position_state'; import { isValidDatemath, datemathToEpochMillis } from '../../../utils/datemath'; /** @@ -35,7 +35,7 @@ export const WithLogPositionUrlState = () => { endDateExpression, updateDateRange, initialize, - } = useContext(LogPositionState.Context); + } = useLogPositionStateContext(); const urlState = useMemo( () => ({ position: visibleMidpoint ? pickTimeKey(visibleMidpoint) : null, diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts b/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts index 1b6d9f850187a..e36ef1caa2c66 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts @@ -5,12 +5,11 @@ * 2.0. */ -import { useContext } from 'react'; import useThrottle from 'react-use/lib/useThrottle'; import { useLogViewContext } from '../../../hooks/use_log_view'; import { RendererFunction } from '../../../utils/typed_react'; -import { LogFilterState } from '../log_filter'; -import { LogPositionState } from '../log_position'; +import { useLogFilterStateContext } from '../log_filter'; +import { useLogPositionStateContext } from '../log_position'; import { LogSummaryBuckets, useLogSummary } from './log_summary'; const FETCH_THROTTLE_INTERVAL = 3000; @@ -25,8 +24,8 @@ export const WithSummary = ({ }>; }) => { const { logViewId } = useLogViewContext(); - const { filterQuery } = useContext(LogFilterState.Context); - const { startTimestamp, endTimestamp } = useContext(LogPositionState.Context); + const { filterQuery } = useLogFilterStateContext(); + const { startTimestamp, endTimestamp } = useLogPositionStateContext(); // Keep it reasonably updated for the `now` case, but don't reload all the time when the user scrolls const throttledStartTimestamp = useThrottle(startTimestamp, FETCH_THROTTLE_INTERVAL); diff --git a/x-pack/plugins/infra/public/containers/logs/log_view_configuration.tsx b/x-pack/plugins/infra/public/containers/logs/log_view_configuration.tsx index 98672d6d14861..543cc61d78977 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_view_configuration.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_view_configuration.tsx @@ -26,7 +26,8 @@ export const useLogViewConfiguration = () => { }; }; -export const LogViewConfiguration = createContainer(useLogViewConfiguration); +export const [LogViewConfigurationProvider, useLogViewConfigurationContext] = + createContainer(useLogViewConfiguration); /** * constants diff --git a/x-pack/plugins/infra/public/containers/logs/view_log_in_context/view_log_in_context.ts b/x-pack/plugins/infra/public/containers/logs/view_log_in_context/view_log_in_context.ts index a389589488d92..b22a73d77cc54 100644 --- a/x-pack/plugins/infra/public/containers/logs/view_log_in_context/view_log_in_context.ts +++ b/x-pack/plugins/infra/public/containers/logs/view_log_in_context/view_log_in_context.ts @@ -42,4 +42,5 @@ export const useViewLogInContext = ( ]; }; -export const ViewLogInContext = createContainer(useViewLogInContext); +export const [ViewLogInContextProvider, useViewLogInProviderContext] = + createContainer(useViewLogInContext); diff --git a/x-pack/plugins/infra/public/containers/logs/with_log_textview.tsx b/x-pack/plugins/infra/public/containers/logs/with_log_textview.tsx index c981a113bf2f4..f57368709467d 100644 --- a/x-pack/plugins/infra/public/containers/logs/with_log_textview.tsx +++ b/x-pack/plugins/infra/public/containers/logs/with_log_textview.tsx @@ -5,10 +5,14 @@ * 2.0. */ -import React, { useContext, useMemo } from 'react'; +import React, { useMemo } from 'react'; import { UrlStateContainer } from '../../utils/url_state'; -import { availableTextScales, LogViewConfiguration, TextScale } from './log_view_configuration'; +import { + availableTextScales, + useLogViewConfigurationContext, + TextScale, +} from './log_view_configuration'; interface LogTextviewUrlState { textScale?: TextScale; @@ -16,9 +20,7 @@ interface LogTextviewUrlState { } export const WithLogTextviewUrlState = () => { - const { textScale, textWrap, setTextScale, setTextWrap } = useContext( - LogViewConfiguration.Context - ); + const { textScale, textWrap, setTextScale, setTextWrap } = useLogViewConfigurationContext(); const urlState = useMemo(() => ({ textScale, wrap: textWrap }), [textScale, textWrap]); diff --git a/x-pack/plugins/infra/public/containers/metrics_explorer/with_metrics_explorer_options_url_state.tsx b/x-pack/plugins/infra/public/containers/metrics_explorer/with_metrics_explorer_options_url_state.tsx index 56de138d19b51..b192bf54e4829 100644 --- a/x-pack/plugins/infra/public/containers/metrics_explorer/with_metrics_explorer_options_url_state.tsx +++ b/x-pack/plugins/infra/public/containers/metrics_explorer/with_metrics_explorer_options_url_state.tsx @@ -6,12 +6,12 @@ */ import { set } from '@kbn/safer-lodash-set'; -import React, { useContext, useMemo } from 'react'; +import React, { useMemo } from 'react'; import { ThrowReporter } from 'io-ts/lib/ThrowReporter'; import { UrlStateContainer } from '../../utils/url_state'; import { MetricsExplorerOptions, - MetricsExplorerOptionsContainer, + useMetricsExplorerOptionsContainerContext, MetricsExplorerTimeOptions, MetricsExplorerChartOptions, metricExplorerOptionsRT, @@ -33,7 +33,7 @@ export const WithMetricsExplorerOptionsUrlState = () => { currentTimerange, setOptions: setRawOptions, setTimeRange, - } = useContext(MetricsExplorerOptionsContainer.Context); + } = useMetricsExplorerOptionsContainerContext(); const setOptions = (value: MetricsExplorerOptions) => { setRawOptions(value); diff --git a/x-pack/plugins/infra/public/containers/metrics_source/source.tsx b/x-pack/plugins/infra/public/containers/metrics_source/source.tsx index 393cad266a123..6a7c7aedccc62 100644 --- a/x-pack/plugins/infra/public/containers/metrics_source/source.tsx +++ b/x-pack/plugins/infra/public/containers/metrics_source/source.tsx @@ -157,5 +157,4 @@ export const useSource = ({ sourceId }: { sourceId: string }) => { }; }; -export const Source = createContainer(useSource); -export const [SourceProvider, useSourceContext] = Source; +export const [SourceProvider, useSourceContext] = createContainer(useSource); diff --git a/x-pack/plugins/infra/public/containers/saved_view/saved_view.tsx b/x-pack/plugins/infra/public/containers/saved_view/saved_view.tsx index e2bb50e0dc2da..8eb3d4c50de1a 100644 --- a/x-pack/plugins/infra/public/containers/saved_view/saved_view.tsx +++ b/x-pack/plugins/infra/public/containers/saved_view/saved_view.tsx @@ -10,14 +10,14 @@ import * as rt from 'io-ts'; import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; import { constant, identity } from 'fp-ts/lib/function'; -import { useCallback, useMemo, useState, useEffect, useContext } from 'react'; +import { useCallback, useMemo, useState, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { SimpleSavedObject, SavedObjectAttributes } from '@kbn/core/public'; import { useUrlState } from '../../utils/use_url_state'; import { useFindSavedObject } from '../../hooks/use_find_saved_object'; import { useCreateSavedObject } from '../../hooks/use_create_saved_object'; import { useDeleteSavedObject } from '../../hooks/use_delete_saved_object'; -import { Source } from '../metrics_source'; +import { useSourceContext } from '../metrics_source'; import { metricsExplorerViewSavedObjectName } from '../../../common/saved_objects/metrics_explorer_view'; import { inventoryViewSavedObjectName } from '../../../common/saved_objects/inventory_view'; import { useSourceConfigurationFormState } from '../../pages/metrics/settings/source_configuration_form_state'; @@ -59,7 +59,7 @@ export const useSavedView = (props: Props) => { sourceExists, createSourceConfiguration, updateSourceConfiguration, - } = useContext(Source.Context); + } = useSourceContext(); const { viewType, defaultViewState } = props; type ViewState = typeof defaultViewState; const { diff --git a/x-pack/plugins/infra/public/containers/with_source/with_source.tsx b/x-pack/plugins/infra/public/containers/with_source/with_source.tsx index 148b679018c7d..b2730cbf2e880 100644 --- a/x-pack/plugins/infra/public/containers/with_source/with_source.tsx +++ b/x-pack/plugins/infra/public/containers/with_source/with_source.tsx @@ -5,14 +5,14 @@ * 2.0. */ -import React, { useContext } from 'react'; +import React from 'react'; import { MetricsSourceConfigurationProperties, PartialMetricsSourceConfigurationProperties, } from '../../../common/metrics_sources'; import { RendererFunction } from '../../utils/typed_react'; -import { Source } from '../metrics_source'; +import { useSourceContext } from '../metrics_source'; import { CreateDerivedIndexPattern } from '../metrics_source'; interface WithSourceProps { @@ -52,7 +52,7 @@ export const WithSource: React.FunctionComponent = ({ children loadSourceFailureMessage, updateSourceConfiguration, version, - } = useContext(Source.Context); + } = useSourceContext(); return children({ create: createSourceConfiguration, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx index 13dd004a1a5ce..6dff4b41ecf53 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx @@ -22,7 +22,7 @@ import { DatasetsSelector } from '../../../components/logging/log_analysis_resul import { RecreateJobButton } from '../../../components/logging/log_analysis_setup/create_job_button'; import { useLogAnalysisCapabilitiesContext } from '../../../containers/logs/log_analysis/log_analysis_capabilities'; import { useLogEntryCategoriesModuleContext } from '../../../containers/logs/log_analysis/modules/log_entry_categories'; -import { ViewLogInContext } from '../../../containers/logs/view_log_in_context'; +import { ViewLogInContextProvider } from '../../../containers/logs/view_log_in_context'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; import { useLogViewContext } from '../../../hooks/use_log_view'; import { LogsPageTemplate } from '../page_template'; @@ -205,7 +205,7 @@ export const LogEntryCategoriesResultsContent: React.FunctionComponent< }); return ( - - + ); }; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_example_message.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_example_message.tsx index be5806d2b3064..091cbb1ba07b7 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_example_message.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/category_example_message.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState, useCallback, useContext } from 'react'; +import React, { useState, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { encode } from 'rison-node'; import moment from 'moment'; @@ -17,7 +17,7 @@ import { getFriendlyNameForPartitionId, partitionField, } from '../../../../../../common/log_analysis'; -import { ViewLogInContext } from '../../../../../containers/logs/view_log_in_context'; +import { useViewLogInProviderContext } from '../../../../../containers/logs/view_log_in_context'; import { LogEntryColumn, LogEntryFieldColumn, @@ -41,7 +41,7 @@ export const CategoryExampleMessage: React.FunctionComponent<{ context: LogEntryContext; }> = ({ id, dataset, message, timestamp, timeRange, tiebreaker, context }) => { const trackMetric = useUiTracker({ app: 'infra_logs' }); - const [, { setContextEntry }] = useContext(ViewLogInContext.Context); + const [, { setContextEntry }] = useViewLogInProviderContext(); // handle special cases for the dataset value const humanFriendlyDataset = getFriendlyNameForPartitionId(dataset); diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx index 53e74ecf54320..f944b089d5d7d 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx @@ -11,7 +11,7 @@ import { LogSourceErrorPage } from '../../../components/logging/log_source_error import { SourceLoadingPage } from '../../../components/source_loading_page'; import { LogEntryCategoriesModuleProvider } from '../../../containers/logs/log_analysis/modules/log_entry_categories'; import { LogEntryRateModuleProvider } from '../../../containers/logs/log_analysis/modules/log_entry_rate'; -import { LogFlyout } from '../../../containers/logs/log_flyout'; +import { LogEntryFlyoutProvider } from '../../../containers/logs/log_flyout'; import { useActiveKibanaSpace } from '../../../hooks/use_kibana_space'; import { useLogViewContext } from '../../../hooks/use_log_view'; @@ -38,7 +38,7 @@ export const LogEntryRatePageProviders: React.FunctionComponent = ({ children }) return ; } else if (resolvedLogView != null) { return ( - + {children} - + ); } else { return null; diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx index 2cb7b2c6f5a7b..18ac30bb35e14 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx @@ -7,7 +7,7 @@ import { EuiSpacer } from '@elastic/eui'; import type { Query } from '@kbn/es-query'; -import React, { useCallback, useContext, useEffect, useMemo } from 'react'; +import React, { useCallback, useEffect, useMemo } from 'react'; import usePrevious from 'react-use/lib/usePrevious'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { LogEntry } from '../../../../common/log_entry'; @@ -18,17 +18,17 @@ import { LogMinimap } from '../../../components/logging/log_minimap'; import { ScrollableLogTextStreamView } from '../../../components/logging/log_text_stream'; import { LogEntryStreamItem } from '../../../components/logging/log_text_stream/item'; import { PageContent } from '../../../components/page'; -import { LogFilterState } from '../../../containers/logs/log_filter'; +import { useLogFilterStateContext } from '../../../containers/logs/log_filter'; import { useLogEntryFlyoutContext, WithFlyoutOptionsUrlState, } from '../../../containers/logs/log_flyout'; -import { LogHighlightsState } from '../../../containers/logs/log_highlights'; -import { LogPositionState } from '../../../containers/logs/log_position'; +import { useLogHighlightsStateContext } from '../../../containers/logs/log_highlights'; +import { useLogPositionStateContext } from '../../../containers/logs/log_position'; import { useLogStreamContext } from '../../../containers/logs/log_stream'; import { WithSummary } from '../../../containers/logs/log_summary'; -import { LogViewConfiguration } from '../../../containers/logs/log_view_configuration'; -import { ViewLogInContext } from '../../../containers/logs/view_log_in_context'; +import { useLogViewConfigurationContext } from '../../../containers/logs/log_view_configuration'; +import { useViewLogInProviderContext } from '../../../containers/logs/view_log_in_context'; import { WithLogTextviewUrlState } from '../../../containers/logs/with_log_textview'; import { useLogViewContext } from '../../../hooks/use_log_view'; import { datemathToEpochMillis, isValidDatemath } from '../../../utils/datemath'; @@ -39,7 +39,7 @@ const PAGE_THRESHOLD = 2; export const LogsPageLogsContent: React.FunctionComponent = () => { const { resolvedLogView, logView, logViewId } = useLogViewContext(); - const { textScale, textWrap } = useContext(LogViewConfiguration.Context); + const { textScale, textWrap } = useLogViewConfigurationContext(); const { surroundingLogsId, setSurroundingLogsId, @@ -64,8 +64,8 @@ export const LogsPageLogsContent: React.FunctionComponent = () => { endDateExpression, updateDateRange, lastCompleteDateRangeExpressionUpdate, - } = useContext(LogPositionState.Context); - const { filterQuery, applyLogFilterQuery } = useContext(LogFilterState.Context); + } = useLogPositionStateContext(); + const { filterQuery, applyLogFilterQuery } = useLogFilterStateContext(); const { isReloading, @@ -132,9 +132,8 @@ export const LogsPageLogsContent: React.FunctionComponent = () => { prevLastCompleteDateRangeExpressionUpdate, ]); - const { logSummaryHighlights, currentHighlightKey, logEntryHighlightsById } = useContext( - LogHighlightsState.Context - ); + const { logSummaryHighlights, currentHighlightKey, logEntryHighlightsById } = + useLogHighlightsStateContext(); const items = useMemo( () => @@ -147,7 +146,7 @@ export const LogsPageLogsContent: React.FunctionComponent = () => { [entries, isReloading, logEntryHighlightsById] ); - const [, { setContextEntry }] = useContext(ViewLogInContext.Context); + const [, { setContextEntry }] = useViewLogInProviderContext(); const handleDateRangeExtension = useCallback( (newDateRange) => { diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx index 11aa74ca862ad..ea2af542586a2 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx @@ -5,29 +5,37 @@ * 2.0. */ -import React, { useContext } from 'react'; -import { LogFilterState, WithLogFilterUrlState } from '../../../containers/logs/log_filter'; -import { LogFlyout } from '../../../containers/logs/log_flyout'; -import { LogHighlightsState } from '../../../containers/logs/log_highlights/log_highlights'; -import { LogPositionState, WithLogPositionUrlState } from '../../../containers/logs/log_position'; +import React from 'react'; +import { + LogFilterStateProvider, + useLogFilterStateContext, + WithLogFilterUrlState, +} from '../../../containers/logs/log_filter'; +import { LogEntryFlyoutProvider } from '../../../containers/logs/log_flyout'; +import { LogHighlightsStateProvider } from '../../../containers/logs/log_highlights/log_highlights'; +import { + LogPositionStateProvider, + useLogPositionStateContext, + WithLogPositionUrlState, +} from '../../../containers/logs/log_position'; import { LogStreamProvider, useLogStreamContext } from '../../../containers/logs/log_stream'; -import { LogViewConfiguration } from '../../../containers/logs/log_view_configuration'; -import { ViewLogInContext } from '../../../containers/logs/view_log_in_context'; +import { LogViewConfigurationProvider } from '../../../containers/logs/log_view_configuration'; +import { ViewLogInContextProvider } from '../../../containers/logs/view_log_in_context'; import { useLogViewContext } from '../../../hooks/use_log_view'; -const LogFilterStateProvider: React.FC = ({ children }) => { +const LogFilterState: React.FC = ({ children }) => { const { derivedDataView } = useLogViewContext(); return ( - + {children} - + ); }; -const ViewLogInContextProvider: React.FC = ({ children }) => { - const { startTimestamp, endTimestamp } = useContext(LogPositionState.Context); +const ViewLogInContext: React.FC = ({ children }) => { + const { startTimestamp, endTimestamp } = useLogPositionStateContext(); const { logViewId } = useLogViewContext(); if (!startTimestamp || !endTimestamp) { @@ -35,22 +43,21 @@ const ViewLogInContextProvider: React.FC = ({ children }) => { } return ( - {children} - + ); }; const LogEntriesStateProvider: React.FC = ({ children }) => { const { logViewId } = useLogViewContext(); - const { startTimestamp, endTimestamp, targetPosition, isInitialized } = useContext( - LogPositionState.Context - ); - const { filterQuery } = useContext(LogFilterState.Context); + const { startTimestamp, endTimestamp, targetPosition, isInitialized } = + useLogPositionStateContext(); + const { filterQuery } = useLogFilterStateContext(); // Don't render anything if the date range is incorrect. if (!startTimestamp || !endTimestamp) { @@ -76,10 +83,10 @@ const LogEntriesStateProvider: React.FC = ({ children }) => { ); }; -const LogHighlightsStateProvider: React.FC = ({ children }) => { +const LogHighlightsState: React.FC = ({ children }) => { const { logViewId, logView } = useLogViewContext(); const { topCursor, bottomCursor, entries } = useLogStreamContext(); - const { filterQuery } = useContext(LogFilterState.Context); + const { filterQuery } = useLogFilterStateContext(); const highlightsProps = { sourceId: logViewId, @@ -90,7 +97,7 @@ const LogHighlightsStateProvider: React.FC = ({ children }) => { size: entries.length, filterQuery: filterQuery?.serializedQuery ?? null, }; - return {children}; + return {children}; }; export const LogsPageProviders: React.FunctionComponent = ({ children }) => { @@ -102,19 +109,19 @@ export const LogsPageProviders: React.FunctionComponent = ({ children }) => { } return ( - - - + + + - - + + - {children} + {children} - - - - - + + + + + ); }; diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx index e3b2f01b6712e..6076849b79f12 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx @@ -8,7 +8,7 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { Query } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; -import React, { useContext } from 'react'; +import React from 'react'; import { QueryStringInput } from '@kbn/unified-search-plugin/public'; import { DataView } from '@kbn/data-views-plugin/public'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; @@ -17,21 +17,20 @@ import { LogDatepicker } from '../../../components/logging/log_datepicker'; import { LogHighlightsMenu } from '../../../components/logging/log_highlights_menu'; import { LogTextScaleControls } from '../../../components/logging/log_text_scale_controls'; import { LogTextWrapControls } from '../../../components/logging/log_text_wrap_controls'; -import { LogFilterState } from '../../../containers/logs/log_filter'; -import { LogFlyout } from '../../../containers/logs/log_flyout'; -import { LogHighlightsState } from '../../../containers/logs/log_highlights/log_highlights'; -import { LogPositionState } from '../../../containers/logs/log_position'; -import { LogViewConfiguration } from '../../../containers/logs/log_view_configuration'; +import { useLogFilterStateContext } from '../../../containers/logs/log_filter'; +import { useLogEntryFlyoutContext } from '../../../containers/logs/log_flyout'; +import { useLogHighlightsStateContext } from '../../../containers/logs/log_highlights/log_highlights'; +import { useLogPositionStateContext } from '../../../containers/logs/log_position'; +import { useLogViewConfigurationContext } from '../../../containers/logs/log_view_configuration'; import { useLogViewContext } from '../../../hooks/use_log_view'; export const LogsToolbar = () => { const { derivedDataView } = useLogViewContext(); - const { availableTextScales, setTextScale, setTextWrap, textScale, textWrap } = useContext( - LogViewConfiguration.Context - ); + const { availableTextScales, setTextScale, setTextWrap, textScale, textWrap } = + useLogViewConfigurationContext(); const { filterQueryDraft, isFilterQueryDraftValid, applyLogFilterQuery, setLogFilterQueryDraft } = - useContext(LogFilterState.Context); - const { setSurroundingLogsId } = useContext(LogFlyout.Context); + useLogFilterStateContext(); + const { setSurroundingLogsId } = useLogEntryFlyoutContext(); const { setHighlightTerms, @@ -41,7 +40,7 @@ export const LogsToolbar = () => { hasNextHighlight, goToPreviousHighlight, goToNextHighlight, - } = useContext(LogHighlightsState.Context); + } = useLogHighlightsStateContext(); const { isStreaming, startLiveStreaming, @@ -49,7 +48,7 @@ export const LogsToolbar = () => { startDateExpression, endDateExpression, updateDateRange, - } = useContext(LogPositionState.Context); + } = useLogPositionStateContext(); return (
diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx index 7afc8e19c43ee..9b4a2ffb4185b 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx @@ -15,10 +15,10 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { isEmpty } from 'lodash'; -import React, { useCallback, useContext, useMemo } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { LogEntry } from '../../../../common/log_entry'; -import { ViewLogInContext } from '../../../containers/logs/view_log_in_context'; +import { useViewLogInProviderContext } from '../../../containers/logs/view_log_in_context'; import { useViewportDimensions } from '../../../utils/use_viewport_dimensions'; import { LogStream } from '../../../components/log_stream'; @@ -26,7 +26,7 @@ const MODAL_MARGIN = 25; export const PageViewLogInContext: React.FC = () => { const [{ contextEntry, startTimestamp, endTimestamp, sourceId }, { setContextEntry }] = - useContext(ViewLogInContext.Context); + useViewLogInProviderContext(); const closeModal = useCallback(() => setContextEntry(undefined), [setContextEntry]); const { width: vw, height: vh } = useViewportDimensions(); diff --git a/x-pack/plugins/infra/public/pages/metrics/index.tsx b/x-pack/plugins/infra/public/pages/metrics/index.tsx index f4d42b5994eb5..e2f4705f17b7d 100644 --- a/x-pack/plugins/infra/public/pages/metrics/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/index.tsx @@ -20,11 +20,12 @@ import { HelpCenterContent } from '../../components/help_center_content'; import { useReadOnlyBadge } from '../../hooks/use_readonly_badge'; import { MetricsExplorerOptionsContainer, + useMetricsExplorerOptionsContainerContext, DEFAULT_METRICS_EXPLORER_VIEW_STATE, } from './metrics_explorer/hooks/use_metrics_explorer_options'; import { WithMetricsExplorerOptionsUrlState } from '../../containers/metrics_explorer/with_metrics_explorer_options_url_state'; import { WithSource } from '../../containers/with_source'; -import { Source } from '../../containers/metrics_source'; +import { SourceProvider } from '../../containers/metrics_source'; import { MetricsExplorerPage } from './metrics_explorer'; import { SnapshotPage } from './inventory_view'; import { MetricDetail } from './metric_detail'; @@ -65,7 +66,7 @@ export const InfrastructurePage = ({ match }: RouteComponentProps) => { return ( - + @@ -109,7 +110,7 @@ export const InfrastructurePage = ({ match }: RouteComponentProps) => { render={(props) => ( {({ configuration, createDerivedIndexPattern }) => ( - + {configuration ? ( { ) : ( )} - + )} )} @@ -132,7 +133,7 @@ export const InfrastructurePage = ({ match }: RouteComponentProps) => { - + ); }; @@ -142,7 +143,7 @@ const PageContent = (props: { createDerivedIndexPattern: CreateDerivedIndexPattern; }) => { const { createDerivedIndexPattern, configuration } = props; - const { options } = useContext(MetricsExplorerOptionsContainer.Context); + const { options } = useMetricsExplorerOptionsContainerContext(); return ( { const nodeId = props.node.id; const nodeType = props.nodeType as InventoryItemType; const inventoryModel = findInventoryModel(nodeType); - const { sourceId } = useContext(Source.Context); + const { sourceId } = useSourceContext(); const { currentTimeRange } = useWaffleTimeContext(); const { loading, metadata } = useMetadata( nodeId, diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/index.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/index.tsx index 22c2af5496a60..65fa3bcfd0164 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/index.tsx @@ -5,12 +5,12 @@ * 2.0. */ -import React, { useCallback, useContext, useMemo } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiLoadingChart } from '@elastic/eui'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { TabContent, TabProps } from '../shared'; -import { Source } from '../../../../../../../containers/metrics_source'; +import { useSourceContext } from '../../../../../../../containers/metrics_source'; import { findInventoryModel } from '../../../../../../../../common/inventory_models'; import { InventoryItemType } from '../../../../../../../../common/inventory_models/types'; import { useMetadata } from '../../../../../metric_detail/hooks/use_metadata'; @@ -23,7 +23,7 @@ const TabComponent = (props: TabProps) => { const nodeId = props.node.id; const nodeType = props.nodeType as InventoryItemType; const inventoryModel = findInventoryModel(nodeType); - const { sourceId } = useContext(Source.Context); + const { sourceId } = useSourceContext(); const { currentTimeRange } = useWaffleTimeContext(); const { applyFilterQuery } = useWaffleFiltersContext(); const { loading: metadataLoading, metadata } = useMetadata( diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/search_bar.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/search_bar.tsx index 1b9e33a0c4dfb..7386322774442 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/search_bar.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/search_bar.tsx @@ -5,15 +5,15 @@ * 2.0. */ -import React, { useContext } from 'react'; +import React from 'react'; import { i18n } from '@kbn/i18n'; -import { Source } from '../../../../containers/metrics_source'; +import { useSourceContext } from '../../../../containers/metrics_source'; import { AutocompleteField } from '../../../../components/autocomplete_field'; import { WithKueryAutocompletion } from '../../../../containers/with_kuery_autocompletion'; import { useWaffleFiltersContext } from '../hooks/use_waffle_filters'; export const SearchBar = () => { - const { createDerivedIndexPattern } = useContext(Source.Context); + const { createDerivedIndexPattern } = useSourceContext(); const { applyFilterQueryFromKueryExpression, filterQueryDraft, diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/index.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/index.tsx index 47e8d739d73e8..0c00b87722c8c 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/index.tsx @@ -7,7 +7,7 @@ import { EuiErrorBoundary } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { useContext } from 'react'; +import React from 'react'; import { useTrackPageview } from '@kbn/observability-plugin/public'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { APP_WRAPPER_CLASS } from '@kbn/core/public'; @@ -17,7 +17,7 @@ import { DocumentTitle } from '../../../components/document_title'; import { SourceErrorPage } from '../../../components/source_error_page'; import { SourceLoadingPage } from '../../../components/source_loading_page'; -import { Source } from '../../../containers/metrics_source'; +import { useSourceContext } from '../../../containers/metrics_source'; import { useMetricsBreadcrumbs } from '../../../hooks/use_metrics_breadcrumbs'; import { LayoutView } from './components/layout_view'; import { SavedViewProvider } from '../../../containers/saved_view/saved_view'; @@ -36,7 +36,7 @@ export const SnapshotPage = () => { loadSource, source, metricIndicesExist, - } = useContext(Source.Context); + } = useSourceContext(); useTrackPageview({ app: 'infra_metrics', path: 'inventory' }); useTrackPageview({ app: 'infra_metrics', path: 'inventory', delay: 15000 }); const { source: optionsSource } = useWaffleOptionsContext(); diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx index cf766bdb104e5..339374678ab7f 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx @@ -5,11 +5,11 @@ * 2.0. */ -import React, { useCallback, useEffect, useState, useContext } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import dateMath from '@kbn/datemath'; import moment from 'moment'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { Source } from '../../../../containers/metrics_source'; +import { useSourceContext } from '../../../../containers/metrics_source'; import { InventoryMetric, InventoryItemType } from '../../../../../common/inventory_models/types'; import { useNodeDetails } from '../hooks/use_node_details'; import { MetricsSideNav } from './side_nav'; @@ -53,7 +53,7 @@ const parseRange = (range: MetricsTimeInput) => { }; export const NodeDetailsPage = (props: Props) => { - const { metricIndicesExist } = useContext(Source.Context); + const { metricIndicesExist } = useSourceContext(); const [parsedTimeRange, setParsedTimeRange] = useState(parseRange(props.timeRange)); const { metrics, loading, makeRequest, error } = useNodeDetails( props.requiredMetrics, diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/index.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/index.tsx index e6843b98cf701..823b9d703f502 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/index.tsx @@ -6,14 +6,14 @@ */ import { i18n } from '@kbn/i18n'; -import React, { useContext, useState } from 'react'; +import React, { useState } from 'react'; import { EuiTheme, withTheme } from '@kbn/kibana-react-plugin/common'; import { useLinkProps } from '@kbn/observability-plugin/public'; import { DocumentTitle } from '../../../components/document_title'; import { withMetricPageProviders } from './page_providers'; import { useMetadata } from './hooks/use_metadata'; import { useMetricsBreadcrumbs } from '../../../hooks/use_metrics_breadcrumbs'; -import { Source } from '../../../containers/metrics_source'; +import { useSourceContext } from '../../../containers/metrics_source'; import { InfraLoadingPanel } from '../../../components/loading'; import { findInventoryModel } from '../../../../common/inventory_models'; import { NavItem } from './lib/side_nav_context'; @@ -38,7 +38,7 @@ export const MetricDetail = withMetricPageProviders( const nodeId = match.params.node; const nodeType = match.params.type as InventoryItemType; const inventoryModel = findInventoryModel(nodeType); - const { sourceId, metricIndicesExist } = useContext(Source.Context); + const { sourceId, metricIndicesExist } = useSourceContext(); const { timeRange, diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/page_providers.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/page_providers.tsx index 6f44d49ddce90..eec4c7db0977b 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/page_providers.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/page_providers.tsx @@ -7,7 +7,7 @@ import { EuiErrorBoundary } from '@elastic/eui'; import React from 'react'; -import { Source } from '../../../containers/metrics_source'; +import { SourceProvider } from '../../../containers/metrics_source'; import { MetricsTimeProvider } from './hooks/use_metrics_time'; export const withMetricPageProviders = @@ -15,10 +15,10 @@ export const withMetricPageProviders = (props: T) => ( - + - + ); diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.test.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.test.tsx index 67d3a71b6c1d4..3cd74c3560aa2 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.test.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.test.tsx @@ -25,9 +25,7 @@ const renderUseMetricsExplorerStateHook = () => renderHook((props) => useMetricsExplorerState(props.source, props.derivedIndexPattern), { initialProps: { source, derivedIndexPattern }, wrapper: ({ children }) => ( - - {children} - + {children} ), }); diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.ts index 39eb7c928997b..4842125a6ff0c 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useState, useCallback, useContext } from 'react'; +import { useState, useCallback } from 'react'; import { DataViewBase } from '@kbn/es-query'; import { MetricsSourceConfigurationProperties } from '../../../../../common/metrics_sources'; import { @@ -14,7 +14,7 @@ import { } from '../../../../../common/http_api/metrics_explorer'; import { useMetricsExplorerData } from './use_metrics_explorer_data'; import { - MetricsExplorerOptionsContainer, + useMetricsExplorerOptionsContainerContext, MetricsExplorerChartOptions, MetricsExplorerTimeOptions, MetricsExplorerOptions, @@ -42,7 +42,7 @@ export const useMetricsExplorerState = ( setChartOptions, setTimeRange, setOptions, - } = useContext(MetricsExplorerOptionsContainer.Context); + } = useMetricsExplorerOptionsContainerContext(); const { loading, error, data, loadData } = useMetricsExplorerData( options, diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options.ts index 8bf64edcf8970..02cab766c57e9 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options.ts @@ -208,4 +208,5 @@ export const useMetricsExplorerOptions = () => { }; }; -export const MetricsExplorerOptionsContainer = createContainer(useMetricsExplorerOptions); +export const [MetricsExplorerOptionsContainer, useMetricsExplorerOptionsContainerContext] = + createContainer(useMetricsExplorerOptions); diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/index.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/index.tsx index a475ae1c88c9a..f5269b99fd5c9 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/index.tsx @@ -7,7 +7,7 @@ import { EuiErrorBoundary } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { useEffect, useContext } from 'react'; +import React, { useEffect } from 'react'; import { useTrackPageview } from '@kbn/observability-plugin/public'; import { MetricsSourceConfigurationProperties } from '../../../../common/metrics_sources'; import { useMetricsBreadcrumbs } from '../../../hooks/use_metrics_breadcrumbs'; @@ -16,7 +16,7 @@ import { NoData } from '../../../components/empty_states'; import { MetricsExplorerCharts } from './components/charts'; import { MetricsExplorerToolbar } from './components/toolbar'; import { useMetricsExplorerState } from './hooks/use_metric_explorer_state'; -import { Source } from '../../../containers/metrics_source'; +import { useSourceContext } from '../../../containers/metrics_source'; import { useSavedViewContext } from '../../../containers/saved_view/saved_view'; import { MetricsPageTemplate } from '../page_template'; import { metricsExplorerTitle } from '../../../translations'; @@ -51,7 +51,7 @@ export const MetricsExplorerPage = ({ source, derivedIndexPattern }: MetricsExpl useTrackPageview({ app: 'infra_metrics', path: 'metrics_explorer' }); useTrackPageview({ app: 'infra_metrics', path: 'metrics_explorer', delay: 15000 }); - const { metricIndicesExist } = useContext(Source.Context); + const { metricIndicesExist } = useSourceContext(); useEffect(() => { if (currentView) { onViewStateChange(currentView); diff --git a/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_settings.tsx b/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_settings.tsx index eab8a82bc2d1e..1b21dfbc800f3 100644 --- a/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_settings.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_settings.tsx @@ -15,10 +15,10 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import React, { useCallback, useContext, useMemo } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { Prompt } from '@kbn/observability-plugin/public'; import { SourceLoadingPage } from '../../../components/source_loading_page'; -import { Source } from '../../../containers/metrics_source'; +import { useSourceContext } from '../../../containers/metrics_source'; import { useInfraMLCapabilitiesContext } from '../../../containers/ml/infra_ml_capabilities'; import { IndicesConfigurationPanel } from './indices_configuration_panel'; import { MLConfigurationPanel } from './ml_configuration_panel'; @@ -48,7 +48,7 @@ export const SourceConfigurationSettings = ({ isLoading, isUninitialized, updateSourceConfiguration, - } = useContext(Source.Context); + } = useSourceContext(); const { indicesConfigurationProps, diff --git a/x-pack/plugins/kubernetes_security/public/components/container_name_widget/hooks.ts b/x-pack/plugins/kubernetes_security/public/components/container_name_widget/hooks.ts index 66dcbc5b50265..c1b7c091b17a4 100644 --- a/x-pack/plugins/kubernetes_security/public/components/container_name_widget/hooks.ts +++ b/x-pack/plugins/kubernetes_security/public/components/container_name_widget/hooks.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useInfiniteQuery } from 'react-query'; +import { useInfiniteQuery } from '@tanstack/react-query'; import { CoreStart } from '@kbn/core/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { QUERY_KEY_CONTAINER_NAME_WIDGET, AGGREGATE_ROUTE } from '../../../common/constants'; diff --git a/x-pack/plugins/kubernetes_security/public/components/count_widget/hooks.ts b/x-pack/plugins/kubernetes_security/public/components/count_widget/hooks.ts index d194e74ae8c62..936419d06c703 100644 --- a/x-pack/plugins/kubernetes_security/public/components/count_widget/hooks.ts +++ b/x-pack/plugins/kubernetes_security/public/components/count_widget/hooks.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useInfiniteQuery } from 'react-query'; +import { useInfiniteQuery } from '@tanstack/react-query'; import { CoreStart } from '@kbn/core/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { QUERY_KEY_COUNT_WIDGET, COUNT_ROUTE } from '../../../common/constants'; diff --git a/x-pack/plugins/kubernetes_security/public/components/percent_widget/hooks.ts b/x-pack/plugins/kubernetes_security/public/components/percent_widget/hooks.ts index f13cdfa0ac7a4..5f5bf99ffd26f 100644 --- a/x-pack/plugins/kubernetes_security/public/components/percent_widget/hooks.ts +++ b/x-pack/plugins/kubernetes_security/public/components/percent_widget/hooks.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { CoreStart } from '@kbn/core/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { QUERY_KEY_PERCENT_WIDGET, AGGREGATE_ROUTE } from '../../../common/constants'; diff --git a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/hooks.ts b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/hooks.ts index ef6c849ee9242..0b8411e710728 100644 --- a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/hooks.ts +++ b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/hooks.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { useInfiniteQuery } from 'react-query'; +import { useInfiniteQuery } from '@tanstack/react-query'; import { CoreStart } from '@kbn/core/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { QueryDslQueryContainerBool } from '../../../types'; diff --git a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/index.test.tsx b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/index.test.tsx index 6a4595ddfb9a3..1763a881c2ba9 100644 --- a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/index.test.tsx +++ b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/dynamic_tree_view/index.test.tsx @@ -18,8 +18,6 @@ describe('DynamicTreeView component', () => { let mockedContext: AppContextTestRender; let mockedApi: AppContextTestRender['coreStart']['http']['get']; - const waitForApiCall = () => waitFor(() => expect(mockedApi).toHaveBeenCalled()); - const defaultProps = { globalFilter: { startDate: Date.now().toString(), @@ -69,8 +67,9 @@ describe('DynamicTreeView component', () => { it('should show loading state while retrieving empty data and hide it when settled', async () => { render(); expect(renderResult.queryByText(/loading/i)).toBeInTheDocument(); - await waitForApiCall(); - expect(renderResult.queryByText(/loading/i)).not.toBeInTheDocument(); + await waitFor(() => { + expect(renderResult.queryByText(/loading/i)).not.toBeInTheDocument(); + }); }); }); @@ -92,39 +91,42 @@ describe('DynamicTreeView component', () => { render({ tree, }); - await waitForApiCall(); - expect(mockedApi).toHaveBeenCalledWith( - '/internal/kubernetes_security/multi_terms_aggregate', - { - query: { - groupBys: `[{"field":"${key}"},{"field":"orchestrator.cluster.name","missing":""}]`, - index: '*-logs', - page: 0, - perPage: 50, - query: '{"bool":{"filter":[],"must":[],"must_not":[],"should":[]}}', - }, - } - ); + await waitFor(() => { + expect(mockedApi).toHaveBeenCalledWith( + '/internal/kubernetes_security/multi_terms_aggregate', + { + query: { + groupBys: `[{"field":"${key}"},{"field":"orchestrator.cluster.name","missing":""}]`, + index: '*-logs', + page: 0, + perPage: 50, + query: '{"bool":{"filter":[],"must":[],"must_not":[],"should":[]}}', + }, + } + ); + }); }); it('should render the parent level based on api response', async () => { render({ tree, }); - await waitForApiCall(); - ['awp-demo-gke-main', 'awp-demo-gke-test'].forEach((cluster) => { - expect(renderResult.queryByText(cluster)).toBeInTheDocument(); + await waitFor(() => { + ['awp-demo-gke-main', 'awp-demo-gke-test'].forEach((cluster) => { + expect(renderResult.queryByText(cluster)).toBeInTheDocument(); + }); }); }); it('should trigger a callback when tree node is clicked', async () => { const callback = jest.fn(); render({ tree, onSelect: callback }); - await waitForApiCall(); - renderResult.getByRole('button', { name: 'awp-demo-gke-main' }).click(); + await waitFor(() => { + renderResult.getByRole('button', { name: 'awp-demo-gke-main' }).click(); + }); expect(callback).toHaveBeenCalled(); }); @@ -156,37 +158,43 @@ describe('DynamicTreeView component', () => { it('should make a children api call with filter when parent is expanded', async () => { render({ tree }); - await waitForApiCall(); - renderResult.getByRole('button', { name: parent }).click(); + await waitFor(() => { + renderResult.getByRole('button', { name: parent }).click(); + }); mockedApi.mockResolvedValueOnce(nodeResponseMock); - await waitForApiCall(); - expect(mockedApi).toHaveBeenCalledWith('/internal/kubernetes_security/aggregate', { - query: { - groupBy: 'node', - index: '*-logs', - page: 0, - perPage: 50, - query: `{"bool":{"filter":[{"term":{"orchestrator.cluster.id":"${parent}"}}],"must":[],"must_not":[],"should":[]}}`, - }, + await waitFor(() => { + expect(mockedApi).toHaveBeenCalledWith('/internal/kubernetes_security/aggregate', { + query: { + groupBy: 'node', + index: '*-logs', + page: 0, + perPage: 50, + query: `{"bool":{"filter":[{"term":{"orchestrator.cluster.id":"${parent}"}}],"must":[],"must_not":[],"should":[]}}`, + }, + }); }); }); it('should render children when parent is expanded based on api request', async () => { render({ tree }); - await waitForApiCall(); - renderResult.getByRole('button', { name: parent }).click(); - - mockedApi.mockResolvedValueOnce(nodeResponseMock); + await waitFor(() => { + expect(renderResult.getByRole('button', { name: parent })).toBeTruthy(); + mockedApi.mockResolvedValueOnce(nodeResponseMock); + renderResult.getByRole('button', { name: parent }).click(); + }); // check if children has loading state - expect(renderResult.queryByText(/loading/i)).toBeInTheDocument(); - await waitForApiCall(); + await waitFor(() => { + expect(renderResult.queryByText(/loading/i)).toBeInTheDocument(); + }); - ['default', 'kube-system', 'production', 'qa', 'staging'].forEach((node) => { - expect(renderResult.queryByText(node)).toBeInTheDocument(); + await waitFor(() => { + ['default', 'kube-system', 'production', 'qa', 'staging'].forEach((node) => { + expect(renderResult.queryByText(node)).toBeInTheDocument(); + }); }); }); }); diff --git a/x-pack/plugins/kubernetes_security/public/methods/index.tsx b/x-pack/plugins/kubernetes_security/public/methods/index.tsx index f0a140a612705..362560c3b1ddf 100644 --- a/x-pack/plugins/kubernetes_security/public/methods/index.tsx +++ b/x-pack/plugins/kubernetes_security/public/methods/index.tsx @@ -7,7 +7,7 @@ import React, { lazy, Suspense } from 'react'; import { EuiLoadingSpinner } from '@elastic/eui'; -import { QueryClient, QueryClientProvider } from 'react-query'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { KubernetesSecurityDeps } from '../types'; // Initializing react-query diff --git a/x-pack/plugins/kubernetes_security/public/test/index.tsx b/x-pack/plugins/kubernetes_security/public/test/index.tsx index 6174925b6003c..244560d366ac4 100644 --- a/x-pack/plugins/kubernetes_security/public/test/index.tsx +++ b/x-pack/plugins/kubernetes_security/public/test/index.tsx @@ -8,7 +8,7 @@ import React, { memo, ReactNode, useMemo } from 'react'; import { createMemoryHistory, MemoryHistory } from 'history'; import { render as reactRender, RenderOptions, RenderResult } from '@testing-library/react'; -import { QueryClient, QueryClientProvider, setLogger } from 'react-query'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { Router } from 'react-router-dom'; import { History } from 'history'; import useObservable from 'react-use/lib/useObservable'; @@ -20,15 +20,6 @@ import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; type UiRender = (ui: React.ReactElement, options?: RenderOptions) => RenderResult; -// hide react-query output in console -setLogger({ - error: () => {}, - // eslint-disable-next-line no-console - log: console.log, - // eslint-disable-next-line no-console - warn: console.warn, -}); - /** * Mocked app root context renderer */ @@ -113,6 +104,14 @@ export const createAppRootMockRenderer = (): AppContextTestRender => { cacheTime: Infinity, }, }, + // hide react-query output in console + logger: { + error: () => {}, + // eslint-disable-next-line no-console + log: console.log, + // eslint-disable-next-line no-console + warn: console.warn, + }, }); const AppWrapper: React.FC<{ children: React.ReactElement }> = ({ children }) => ( diff --git a/x-pack/plugins/lens/common/index.ts b/x-pack/plugins/lens/common/index.ts index e0600bd18afc1..45dd41422b018 100644 --- a/x-pack/plugins/lens/common/index.ts +++ b/x-pack/plugins/lens/common/index.ts @@ -8,7 +8,6 @@ // TODO: https://github.com/elastic/kibana/issues/110891 /* eslint-disable @kbn/eslint/no_export_all */ -export * from './api'; export * from './constants'; export * from './types'; diff --git a/x-pack/plugins/lens/kibana.json b/x-pack/plugins/lens/kibana.json index cb208eb60daff..bff30e66a9bc8 100644 --- a/x-pack/plugins/lens/kibana.json +++ b/x-pack/plugins/lens/kibana.json @@ -26,7 +26,8 @@ "expressionMetricVis", "expressionHeatmap", "eventAnnotation", - "unifiedSearch" + "unifiedSearch", + "unifiedFieldList" ], "optionalPlugins": [ "expressionXY", diff --git a/x-pack/plugins/lens/public/app_plugin/app.test.tsx b/x-pack/plugins/lens/public/app_plugin/app.test.tsx index 03cf38f141f0d..7f166cb54e299 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.test.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.test.tsx @@ -1415,7 +1415,8 @@ describe('Lens App', () => { layers: [ { indexPatternId: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', - timeFieldName: 'order_date', + xFieldName: 'order_date', + xMode: 'date_histogram', chartType: 'area', axisPosition: 'left', palette: { diff --git a/x-pack/plugins/lens/public/app_plugin/mounter.tsx b/x-pack/plugins/lens/public/app_plugin/mounter.tsx index 34a06439473a3..8c73d826724ea 100644 --- a/x-pack/plugins/lens/public/app_plugin/mounter.tsx +++ b/x-pack/plugins/lens/public/app_plugin/mounter.tsx @@ -96,6 +96,7 @@ export async function getLensServices( dataViewEditor: startDependencies.dataViewEditor, dataViewFieldEditor: startDependencies.dataViewFieldEditor, dashboard: startDependencies.dashboard, + charts: startDependencies.charts, getOriginatingAppName: () => { return embeddableEditorIncomingState?.originatingApp ? stateTransfer?.getAppNameFromId(embeddableEditorIncomingState.originatingApp) diff --git a/x-pack/plugins/lens/public/app_plugin/types.ts b/x-pack/plugins/lens/public/app_plugin/types.ts index 6a2718fc49820..be49ca768acdc 100644 --- a/x-pack/plugins/lens/public/app_plugin/types.ts +++ b/x-pack/plugins/lens/public/app_plugin/types.ts @@ -37,6 +37,7 @@ import { ACTION_CONVERT_TO_LENS } from '@kbn/visualizations-plugin/public'; import type { EmbeddableEditorState, EmbeddableStateTransfer } from '@kbn/embeddable-plugin/public'; import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +import type { ChartsPluginSetup } from '@kbn/charts-plugin/public'; import type { DatasourceMap, EditorFrameInstance, @@ -140,6 +141,7 @@ export interface LensAppServices { getOriginatingAppName: () => string | undefined; presentationUtil: PresentationUtilPluginStart; spaces: SpacesApi; + charts: ChartsPluginSetup; discover?: DiscoverStart; // Temporarily required until the 'by value' paradigm is default. diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx index e660df8ff7bb9..3ddf2a2cc4b75 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx @@ -17,9 +17,6 @@ import { EuiFlexGroup, EuiFlexItem, EuiFocusTrap, - EuiOutsideClickDetector, - EuiWindowEvent, - keys, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -37,7 +34,10 @@ function fromExcludedClickTarget(event: Event) { node !== null; node = node!.parentElement ) { - if (node.classList!.contains(DONT_CLOSE_DIMENSION_CONTAINER_ON_CLICK_CLASS)) { + if ( + node.classList!.contains(DONT_CLOSE_DIMENSION_CONTAINER_ON_CLICK_CLASS) || + node.classList!.contains('euiBody-hasPortalContent') + ) { return true; } } @@ -69,18 +69,6 @@ export function DimensionContainer({ return canClose; }, [handleClose]); - const closeOnEscape = useCallback( - (event: KeyboardEvent) => { - if (event.key === keys.ESCAPE) { - const canClose = closeFlyout(); - if (canClose) { - event.preventDefault(); - } - } - }, - [closeFlyout] - ); - useEffect(() => { if (isOpen) { document.body.classList.add('lnsBody--overflowHidden'); @@ -94,81 +82,80 @@ export function DimensionContainer({ return isOpen ? (
- - - { - if (isFullscreen || fromExcludedClickTarget(event)) { - return; + { + if (isFullscreen || fromExcludedClickTarget(event)) { + return; + } + closeFlyout(); + }} + onEscapeKey={closeFlyout} + > +
{ + if (isOpen) { + // EuiFocusTrap interferes with animating elements with absolute position: + // running this onAnimationEnd, otherwise the flyout pushes content when animating + setFocusTrapIsEnabled(true); } - closeFlyout(); }} - isDisabled={!isOpen} > -
{ - if (isOpen) { - // EuiFocusTrap interferes with animating elements with absolute position: - // running this onAnimationEnd, otherwise the flyout pushes content when animating - setFocusTrapIsEnabled(true); - } - }} - > - - - - -

- - {i18n.translate('xpack.lens.configure.configurePanelTitle', { - defaultMessage: '{groupLabel}', - values: { - groupLabel, - }, - })} - -

-
-
+ + + + +

+ + {i18n.translate('xpack.lens.configure.configurePanelTitle', { + defaultMessage: '{groupLabel}', + values: { + groupLabel, + }, + })} + +

+
+
- - - -
-
+ + + +
+
-
{panel}
+
{panel}
- - - {i18n.translate('xpack.lens.dimensionContainer.close', { - defaultMessage: 'Close', - })} - - -
- + + + {i18n.translate('xpack.lens.dimensionContainer.close', { + defaultMessage: 'Close', + })} + + +
) : null; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 86e896c29910c..544835d2e3c21 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -614,7 +614,8 @@ export function LayerPanel( setState: updateDataLayerState, supportStaticValue: Boolean(activeGroup.supportStaticValue), paramEditorCustomProps: activeGroup.paramEditorCustomProps, - supportFieldFormat: activeGroup.supportFieldFormat !== false, + enableFormatSelector: activeGroup.enableFormatSelector !== false, + formatSelectorOptions: activeGroup.formatSelectorOptions, layerType: activeVisualization.getLayerType(layerId, visualizationState), indexPatterns: dataViews.indexPatterns, existingFields: dataViews.existingFields, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts index 5cc62012a5094..e566443640d85 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts @@ -288,7 +288,8 @@ describe('suggestion helpers', () => { layers: [ { indexPatternId: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', - timeFieldName: 'order_date', + xFieldName: 'order_date', + xMode: 'date_histogram', chartType: 'area', axisPosition: 'left', palette: { @@ -369,7 +370,8 @@ describe('suggestion helpers', () => { layers: [ { indexPatternId: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', - timeFieldName: 'order_date', + xFieldName: 'order_date', + xMode: 'date_histogram', chartType: 'area', axisPosition: 'left', palette: { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx index 2af3d90b33b95..a87e7cd3cac6c 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx @@ -95,7 +95,8 @@ export function DimensionEditor(props: DimensionEditorProps) { toggleFullscreen, isFullscreen, supportStaticValue, - supportFieldFormat = true, + enableFormatSelector = true, + formatSelectorOptions, layerType, paramEditorCustomProps, } = props; @@ -1010,11 +1011,15 @@ export function DimensionEditor(props: DimensionEditorProps) { /> )} - {supportFieldFormat && + {enableFormatSelector && !isFullscreen && selectedColumn && (selectedColumn.dataType === 'number' || selectedColumn.operationType === 'range') ? ( - + ) : null}
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx index 35bd94d448270..b64e12b7bf0a4 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx @@ -244,6 +244,8 @@ describe('IndexPatternDimensionEditorPanel', () => { isFullscreen: false, supportStaticValue: false, toggleFullscreen: jest.fn(), + enableFormatSelector: true, + formatSelectorOptions: undefined, }; jest.clearAllMocks(); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/format_selector.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/format_selector.test.tsx index 60fdb382322a6..d59cb4f136fda 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/format_selector.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/format_selector.test.tsx @@ -86,4 +86,18 @@ describe('FormatSelector', () => { component.update(); expect(props.onChange).toBeCalledWith({ id: 'bytes', params: { suffix: 'GB' } }); }); + describe('options', () => { + it('can disable the extra options', () => { + const props = getDefaultProps(); + const component = mount( + + ); + expect(component.exists('[data-test-subj="indexPattern-dimension-formatDecimals"]')).toBe( + false + ); + expect(component.exists('[data-test-subj="indexPattern-dimension-formatSuffix"]')).toBe( + false + ); + }); + }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/format_selector.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/format_selector.tsx index 49231e64d53be..5a6fc9aab91eb 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/format_selector.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/format_selector.tsx @@ -51,9 +51,14 @@ const suffixLabel = i18n.translate('xpack.lens.indexPattern.suffixLabel', { defaultMessage: 'Suffix', }); +export interface FormatSelectorOptions { + disableExtraOptions?: boolean; +} + interface FormatSelectorProps { selectedColumn: GenericIndexPatternColumn; onChange: (newFormat?: { id: string; params?: Record }) => void; + options?: FormatSelectorOptions; } const RANGE_MIN = 0; @@ -149,9 +154,9 @@ export function FormatSelector(props: FormatSelectorProps) { selectedOptions={currentOption} onChange={onChangeWrapped} /> - {currentFormat ? ( + {currentFormat && !props.options?.disableExtraOptions ? ( <> - + - + + { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.scss b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.scss index b96a670144d37..a309e37938cda 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.scss +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.scss @@ -50,23 +50,6 @@ color: $euiColorDarkShade; } -.lnsFieldItem__topValue { - margin-bottom: $euiSizeS; - - &:last-of-type { - margin-bottom: 0; - } -} - -.lnsFieldItem__topValueProgress { - background-color: $euiColorLightestShade; - - // sass-lint:disable-block no-vendor-prefixes - &::-webkit-progress-bar { - background-color: $euiColorLightestShade; - } -} - .lnsFieldItem__fieldPanel { min-width: 260px; max-width: 300px; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.test.tsx index 3c3b81cd126df..615279b71b82c 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.test.tsx @@ -18,18 +18,45 @@ import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { documentField } from './document_field'; import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; +import type { DataView } from '@kbn/data-views-plugin/common'; +import { loadFieldStats } from '@kbn/unified-field-list-plugin/public'; import { DOCUMENT_FIELD_NAME } from '../../common'; +jest.mock('@kbn/unified-field-list-plugin/public/services/field_stats', () => ({ + loadFieldStats: jest.fn().mockResolvedValue({}), +})); + const chartsThemeService = chartPluginMock.createSetupContract().theme; -function clickField(wrapper: ReactWrapper, field: string) { - wrapper.find(`[data-test-subj="lnsFieldListPanelField-${field}"] button`).simulate('click'); -} +const clickField = async (wrapper: ReactWrapper, field: string) => { + await act(async () => { + wrapper.find(`[data-test-subj="lnsFieldListPanelField-${field}"] button`).simulate('click'); + }); +}; + +const mockedServices = { + data: dataPluginMock.createStartContract(), + dataViews: dataViewPluginMocks.createStartContract(), + fieldFormats: fieldFormatsServiceMock.createStartContract(), + charts: chartPluginMock.createSetupContract(), + uiSettings: coreMock.createStart().uiSettings, +}; + +const InnerFieldItemWrapper: React.FC = (props) => { + return ( + + + + ); +}; describe('IndexPattern Field Item', () => { let defaultProps: FieldItemProps; let indexPattern: IndexPattern; - let core: ReturnType; + let dataView: DataView; beforeEach(() => { indexPattern = { @@ -83,8 +110,6 @@ describe('IndexPattern Field Item', () => { ], } as IndexPattern; - core = coreMock.createSetup(); - core.http.post.mockClear(); defaultProps = { indexPattern, fieldFormats: { @@ -93,7 +118,7 @@ describe('IndexPattern Field Item', () => { convert: jest.fn((s: unknown) => JSON.stringify(s)), })), } as unknown as FieldFormatsStart, - core, + core: coreMock.createStart(), highlight: '', dateRange: { fromDate: 'now-7d', @@ -116,10 +141,19 @@ describe('IndexPattern Field Item', () => { hasSuggestionForField: () => false, uiActions: uiActionsPluginMock.createStartContract(), }; + + dataView = { + ...indexPattern, + getFormatterForField: defaultProps.fieldFormats.getDefaultInstance, + } as unknown as DataView; + + (mockedServices.dataViews.get as jest.Mock).mockImplementation(() => { + return Promise.resolve(dataView); + }); }); it('should display displayName of a field', () => { - const wrapper = mountWithIntl(); + const wrapper = mountWithIntl(); // Using .toContain over .toEqual because this element includes text from // which can't be seen, but shows in the text content @@ -128,15 +162,12 @@ describe('IndexPattern Field Item', () => { ); }); - it('should render edit field button if callback is set', () => { - core.http.post.mockImplementation(() => { - return new Promise(() => {}); - }); + it('should render edit field button if callback is set', async () => { const editFieldSpy = jest.fn(); const wrapper = mountWithIntl( - + ); - clickField(wrapper, 'bytes'); + await clickField(wrapper, 'bytes'); wrapper.update(); const popoverContent = wrapper.find(EuiPopover).prop('children'); act(() => { @@ -148,20 +179,17 @@ describe('IndexPattern Field Item', () => { expect(editFieldSpy).toHaveBeenCalledWith('bytes'); }); - it('should not render edit field button for document field', () => { - core.http.post.mockImplementation(() => { - return new Promise(() => {}); - }); + it('should not render edit field button for document field', async () => { const editFieldSpy = jest.fn(); const wrapper = mountWithIntl( - ); - clickField(wrapper, documentField.name); + await clickField(wrapper, documentField.name); wrapper.update(); const popoverContent = wrapper.find(EuiPopover).prop('children'); expect( @@ -174,30 +202,33 @@ describe('IndexPattern Field Item', () => { it('should request field stats every time the button is clicked', async () => { let resolveFunction: (arg: unknown) => void; - core.http.post.mockImplementation(() => { + (loadFieldStats as jest.Mock).mockImplementation(() => { return new Promise((resolve) => { resolveFunction = resolve; }); }); - const wrapper = mountWithIntl(); + const wrapper = mountWithIntl(); - clickField(wrapper, 'bytes'); + await clickField(wrapper, 'bytes'); - expect(core.http.post).toHaveBeenCalledWith(`/api/lens/index_stats/1/field`, { - body: JSON.stringify({ - dslQuery: { - bool: { - must: [], - filter: [], - should: [], - must_not: [], - }, + await wrapper.update(); + + expect(loadFieldStats).toHaveBeenCalledWith({ + abortController: new AbortController(), + services: { data: mockedServices.data }, + dataView, + dslQuery: { + bool: { + must: [], + filter: [], + should: [], + must_not: [], }, - fromDate: 'now-7d', - toDate: 'now', - fieldName: 'bytes', - }), + }, + fromDate: 'now-7d', + toDate: 'now', + field: defaultProps.field, }); expect(wrapper.find(EuiPopover).prop('isOpen')).toEqual(true); @@ -218,12 +249,15 @@ describe('IndexPattern Field Item', () => { }); }); - wrapper.update(); + await wrapper.update(); expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(0); - clickField(wrapper, 'bytes'); - expect(core.http.post).toHaveBeenCalledTimes(1); + await clickField(wrapper, 'bytes'); + + await wrapper.update(); + + expect(loadFieldStats).toHaveBeenCalledTimes(1); act(() => { const closePopover = wrapper.find(EuiPopover).prop('closePopover'); @@ -231,6 +265,8 @@ describe('IndexPattern Field Item', () => { closePopover(); }); + await wrapper.update(); + expect(wrapper.find(EuiPopover).prop('isOpen')).toEqual(false); act(() => { @@ -248,49 +284,59 @@ describe('IndexPattern Field Item', () => { }); }); - clickField(wrapper, 'bytes'); - - expect(core.http.post).toHaveBeenCalledTimes(2); - expect(core.http.post).toHaveBeenLastCalledWith(`/api/lens/index_stats/1/field`, { - body: JSON.stringify({ - dslQuery: { - bool: { - must: [], - filter: [ - { - bool: { - should: [{ match_phrase: { 'geo.src': 'US' } }], - minimum_should_match: 1, - }, + await clickField(wrapper, 'bytes'); + + await wrapper.update(); + + expect(loadFieldStats).toHaveBeenCalledTimes(2); + expect(loadFieldStats).toHaveBeenLastCalledWith({ + abortController: new AbortController(), + services: { data: mockedServices.data }, + dataView, + dslQuery: { + bool: { + must: [], + filter: [ + { + bool: { + should: [{ match_phrase: { 'geo.src': 'US' } }], + minimum_should_match: 1, }, - { - match: { phrase: { 'geo.dest': 'US' } }, - }, - ], - should: [], - must_not: [], - }, + }, + { + match: { phrase: { 'geo.dest': 'US' } }, + }, + ], + should: [], + must_not: [], }, - fromDate: 'now-14d', - toDate: 'now-7d', - fieldName: 'bytes', - }), + }, + fromDate: 'now-14d', + toDate: 'now-7d', + field: defaultProps.field, }); + + (loadFieldStats as jest.Mock).mockReset(); + (loadFieldStats as jest.Mock).mockImplementation(() => Promise.resolve({})); }); it('should not request field stats for document field', async () => { - const wrapper = mountWithIntl(); + const wrapper = mountWithIntl( + + ); - clickField(wrapper, DOCUMENT_FIELD_NAME); + await clickField(wrapper, DOCUMENT_FIELD_NAME); - expect(core.http.post).not.toHaveBeenCalled(); + await wrapper.update(); + + expect(loadFieldStats).not.toHaveBeenCalled(); expect(wrapper.find(EuiPopover).prop('isOpen')).toEqual(true); expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(0); }); it('should not request field stats for range fields', async () => { const wrapper = mountWithIntl( - { /> ); - await act(async () => { - clickField(wrapper, 'ip_range'); - }); + await clickField(wrapper, 'ip_range'); - expect(core.http.post).not.toHaveBeenCalled(); + expect(loadFieldStats).not.toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx index 1ec3c3b41ca40..427b8e4623353 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx @@ -8,49 +8,38 @@ import './field_item.scss'; import React, { useCallback, useState, useMemo } from 'react'; -import DateMath from '@kbn/datemath'; import { - EuiButtonGroup, EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiIconTip, - EuiLoadingSpinner, EuiPopover, - EuiPopoverFooter, EuiPopoverTitle, - EuiProgress, + EuiPopoverFooter, EuiSpacer, EuiText, EuiTitle, EuiToolTip, } from '@elastic/eui'; -import { - Axis, - HistogramBarSeries, - Chart, - niceTimeFormatter, - Position, - ScaleType, - Settings, - TooltipType, -} from '@elastic/charts'; import { i18n } from '@kbn/i18n'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; import { FieldButton } from '@kbn/react-field'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { EuiHighlight } from '@elastic/eui'; -import { Filter, buildEsQuery, Query } from '@kbn/es-query'; -import { KBN_FIELD_TYPES, ES_FIELD_TYPES, getEsQueryConfig } from '@kbn/data-plugin/public'; +import { Filter, Query } from '@kbn/es-query'; +import { DataViewField } from '@kbn/data-views-plugin/common'; import { ChartsPluginSetup } from '@kbn/charts-plugin/public'; import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; +import { FieldStats } from '@kbn/unified-field-list-plugin/public'; import { DragDrop, DragDropIdentifier } from '../drag_drop'; -import type { DatasourceDataPanelProps, DataType, IndexPattern, IndexPatternField } from '../types'; -import { BucketedAggregation, DOCUMENT_FIELD_NAME, FieldStatsResponse } from '../../common'; -import { DraggedField } from './types'; +import { DatasourceDataPanelProps, DataType } from '../types'; +import { DOCUMENT_FIELD_NAME } from '../../common'; +import type { IndexPattern, IndexPatternField } from '../types'; +import type { DraggedField } from './types'; import { LensFieldIcon } from '../shared_components/field_picker/lens_field_icon'; import { VisualizeGeoFieldButton } from './visualize_geo_field_button'; import { getVisualizeGeoFieldMessage } from '../utils'; - +import type { LensAppServices } from '../app_plugin/types'; import { debouncedComponent } from '../debounced_component'; export interface FieldItemProps { @@ -74,15 +63,6 @@ export interface FieldItemProps { uiActions: UiActionsStart; } -interface State { - isLoading: boolean; - totalDocuments?: number; - sampledDocuments?: number; - sampledValues?: number; - histogram?: BucketedAggregation; - topValues?: BucketedAggregation; -} - function wrapOnDot(str?: string) { // u200B is a non-width white-space character, which allows // the browser to efficiently word-wrap right after the dot @@ -92,14 +72,10 @@ function wrapOnDot(str?: string) { export const InnerFieldItem = function InnerFieldItem(props: FieldItemProps) { const { - core, field, indexPattern, highlight, exists, - query, - dateRange, - filters, hideDetails, itemIndex, groupIndex, @@ -140,54 +116,8 @@ export const InnerFieldItem = function InnerFieldItem(props: FieldItemProps) { [dropOntoWorkspace, setOpen] ); - const [state, setState] = useState({ - isLoading: false, - }); - - function fetchData() { - // Range types don't have any useful stats we can show - if ( - state.isLoading || - field.type === 'document' || - field.type.includes('range') || - field.type === 'geo_point' || - field.type === 'geo_shape' - ) { - return; - } - - setState((s) => ({ ...s, isLoading: true })); - - core.http - .post>(`/api/lens/index_stats/${indexPattern.id}/field`, { - body: JSON.stringify({ - dslQuery: buildEsQuery(indexPattern, query, filters, getEsQueryConfig(core.uiSettings)), - fromDate: dateRange.fromDate, - toDate: dateRange.toDate, - fieldName: field.name, - }), - }) - .then((results) => { - setState((s) => ({ - ...s, - isLoading: false, - totalDocuments: results.totalDocuments, - sampledDocuments: results.sampledDocuments, - sampledValues: results.sampledValues, - histogram: results.histogram, - topValues: results.topValues, - })); - }) - .catch(() => { - setState((s) => ({ ...s, isLoading: false })); - }); - } - function togglePopover() { setOpen(!infoIsOpen); - if (!infoIsOpen) { - fetchData(); - } } const onDragStart = useCallback(() => { @@ -281,13 +211,14 @@ export const InnerFieldItem = function InnerFieldItem(props: FieldItemProps) { panelClassName="lnsFieldItem__fieldPanel" initialFocus=".lnsFieldItem__fieldPanel" > - + {infoIsOpen && ( + + )} ); @@ -373,45 +304,22 @@ function FieldPanelHeader({ ); } -function FieldItemPopoverContents(props: State & FieldItemProps) { +function FieldItemPopoverContents(props: FieldItemProps) { const { - histogram, - topValues, + query, + filters, indexPattern, field, dateRange, - core, - sampledValues, - chartsThemeService, - fieldFormats, dropOntoWorkspace, editField, removeField, hasSuggestionForField, hideDetails, uiActions, + core, } = props; - - const chartTheme = chartsThemeService.useChartsTheme(); - const chartBaseTheme = chartsThemeService.useChartsBaseTheme(); - let histogramDefault = !!props.histogram; - - const totalValuesCount = - topValues && topValues.buckets.reduce((prev, bucket) => bucket.count + prev, 0); - const otherCount = sampledValues && totalValuesCount ? sampledValues - totalValuesCount : 0; - - if ( - totalValuesCount && - histogram && - histogram.buckets.length && - topValues && - topValues.buckets.length - ) { - // Default to histogram when top values are less than 10% of total - histogramDefault = otherCount / totalValuesCount > 0.9; - } - - const [showingHistogram, setShowingHistogram] = useState(histogramDefault); + const services = useKibana().services; const panelHeader = ( string }; - if (indexPattern.fieldFormatMap && indexPattern.fieldFormatMap[field.name]) { - const FormatType = fieldFormats.getType(indexPattern.fieldFormatMap[field.name].id as string); - if (FormatType) { - formatter = new FormatType( - indexPattern.fieldFormatMap[field.name].params, - core.uiSettings.get.bind(core.uiSettings) - ); - } else { - formatter = { convert: (data: unknown) => JSON.stringify(data) }; - } - } else { - formatter = fieldFormats.getDefaultInstance( - field.type as KBN_FIELD_TYPES, - field.esTypes as ES_FIELD_TYPES[] - ); - } - - const fromDate = DateMath.parse(dateRange.fromDate); - const toDate = DateMath.parse(dateRange.toDate); - - let title = <>; - - if (props.isLoading) { - return ( - <> - {panelHeader} - - - ); - } else if (field.type.includes('range')) { - return ( - <> - {panelHeader} - - - {i18n.translate('xpack.lens.indexPattern.fieldStatsLimited', { - defaultMessage: `Summary information is not available for range type fields.`, - })} - - - ); - } else if (field.type === 'murmur3') { - return ( - <> - {panelHeader} - - - {i18n.translate('xpack.lens.indexPattern.fieldStatsMurmur3Limited', { - defaultMessage: `Summary information is not available for murmur3 fields.`, - })} - - - ); - } else if (field.type === 'geo_point' || field.type === 'geo_shape') { - return ( - <> - {panelHeader} - - {getVisualizeGeoFieldMessage(field.type)} - - - - - ); - } else if ( - (!props.histogram || props.histogram.buckets.length === 0) && - (!props.topValues || props.topValues.buckets.length === 0) - ) { - const isUsingSampling = core.uiSettings.get('lens:useFieldExistenceSampling'); - return ( - <> - {panelHeader} + return ( + <> + {panelHeader} + {element}} + overrideMissingContent={(params) => { + if (field.type === 'geo_point' || field.type === 'geo_shape') { + return ( + <> + {getVisualizeGeoFieldMessage(field.type)} + + + + + ); + } - - {isUsingSampling - ? i18n.translate('xpack.lens.indexPattern.fieldStatsSamplingNoData', { - defaultMessage: - 'Lens is unable to create visualizations with this field because it does not contain data in the first 500 documents that match your filters. To create a visualization, drag and drop a different field.', - }) - : i18n.translate('xpack.lens.indexPattern.fieldStatsNoData', { - defaultMessage: - 'Lens is unable to create visualizations with this field because it does not contain data. To create a visualization, drag and drop a different field.', - })} - - - ); - } + if (params?.noDataFound) { + const isUsingSampling = core.uiSettings.get('lens:useFieldExistenceSampling'); + return ( + <> + + {isUsingSampling + ? i18n.translate('xpack.lens.indexPattern.fieldStatsSamplingNoData', { + defaultMessage: + 'Lens is unable to create visualizations with this field because it does not contain data in the first 500 documents that match your filters. To create a visualization, drag and drop a different field.', + }) + : i18n.translate('xpack.lens.indexPattern.fieldStatsNoData', { + defaultMessage: + 'Lens is unable to create visualizations with this field because it does not contain data. To create a visualization, drag and drop a different field.', + })} + + + ); + } - if (histogram && histogram.buckets.length && topValues && topValues.buckets.length) { - title = ( - { - setShowingHistogram(optionId === 'histogram'); + return null; }} - idSelected={showingHistogram ? 'histogram' : 'topValues'} /> - ); - } else if (field.type === 'date') { - title = ( - -
- {i18n.translate('xpack.lens.indexPattern.fieldTimeDistributionLabel', { - defaultMessage: 'Time distribution', - })} -
-
- ); - } else if (topValues && topValues.buckets.length) { - title = ( - -
- {i18n.translate('xpack.lens.indexPattern.fieldTopValuesLabel', { - defaultMessage: 'Top values', - })} -
-
- ); - } - - function wrapInPopover(el: React.ReactElement) { - return ( - <> - {panelHeader} - - {title ? title : <>} - - - - {el} - - {props.totalDocuments ? ( - - - {props.sampledDocuments && ( - <> - {i18n.translate('xpack.lens.indexPattern.percentageOfLabel', { - defaultMessage: '{percentage}% of', - values: { - percentage: Math.round((props.sampledDocuments / props.totalDocuments) * 100), - }, - })} - - )}{' '} - - {fieldFormats - .getDefaultInstance(KBN_FIELD_TYPES.NUMBER, [ES_FIELD_TYPES.INTEGER]) - .convert(props.totalDocuments)} - {' '} - {i18n.translate('xpack.lens.indexPattern.ofDocumentsLabel', { - defaultMessage: 'documents', - })} - - - ) : ( - <> - )} - - ); - } - - if (histogram && histogram.buckets.length) { - const specId = i18n.translate('xpack.lens.indexPattern.fieldStatsCountLabel', { - defaultMessage: 'Count', - }); - - if (field.type === 'date') { - return wrapInPopover( - - - - - - - - ); - } else if (showingHistogram || !topValues || !topValues.buckets.length) { - return wrapInPopover( - - - - formatter.convert(d)} - /> - - - - ); - } - } - - if (props.topValues && props.topValues.buckets.length) { - const digitsRequired = props.topValues.buckets.some( - (topValue) => !Number.isInteger(topValue.count / props.sampledValues!) - ); - return wrapInPopover( -
- {props.topValues.buckets.map((topValue) => { - const formatted = formatter.convert(topValue.key); - return ( -
- - - {formatted === '' ? ( - - - {i18n.translate('xpack.lens.indexPattern.fieldPanelEmptyStringValue', { - defaultMessage: 'Empty string', - })} - - - ) : ( - - - {formatted} - - - )} - - - - {(Math.round((topValue.count / props.sampledValues!) * 1000) / 10).toFixed( - digitsRequired ? 1 : 0 - )} - % - - - - - -
- ); - })} - {otherCount ? ( - <> - - - - {i18n.translate('xpack.lens.indexPattern.otherDocsLabel', { - defaultMessage: 'Other', - })} - - - - - - {(Math.round((otherCount / props.sampledValues!) * 1000) / 10).toFixed( - digitsRequired ? 1 : 0 - )} - % - - - - - - - ) : ( - <> - )} -
- ); - } - return <>; + + ); } const DragToWorkspaceButton = ({ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index a2ab9fd238215..74f006a0c3174 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -248,17 +248,27 @@ export function getIndexPatternDatasource({ render( - + + + , domElement @@ -586,7 +596,14 @@ export function getIndexPatternDatasource({ .filter(([_, layer]) => !!indexPatterns[layer.indexPatternId]) .map(([layerId, layer]) => ( - getErrorMessages(layer, indexPatterns[layer.indexPatternId], state, layerId, core) ?? [] + getErrorMessages( + layer, + indexPatterns[layer.indexPatternId], + state, + layerId, + core, + data + ) ?? [] ).map((message) => ({ shortMessage: '', // Not displayed currently longMessage: typeof message === 'string' ? message : message.message, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx index 90f59f5470e37..e90ed2c781d5a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx @@ -1504,7 +1504,8 @@ describe('IndexPattern Data Source suggestions', () => { const context = [ { indexPatternId: '1', - timeFieldName: 'timestamp', + xFieldName: 'timestamp', + xMode: 'date_histogram', chartType: 'area', axisPosition: 'left', palette: { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts index 319c309cac036..382df0c9b53dd 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts @@ -178,10 +178,8 @@ function getEmptyLayersSuggestionsForVisualizeCharts( if (!indexPattern) return []; const newId = generateId(); - let newLayer: IndexPatternLayer | undefined; - if (indexPattern.timeFieldName) { - newLayer = createNewTimeseriesLayerWithMetricAggregationFromVizEditor(indexPattern, layer); - } + const newLayer: IndexPatternLayer | undefined = + createNewLayerWithMetricAggregationFromVizEditor(indexPattern, layer); if (newLayer) { const suggestion = buildSuggestion({ state, @@ -197,13 +195,13 @@ function getEmptyLayersSuggestionsForVisualizeCharts( return suggestions; } -function createNewTimeseriesLayerWithMetricAggregationFromVizEditor( +function createNewLayerWithMetricAggregationFromVizEditor( indexPattern: IndexPattern, layer: VisualizeEditorLayersContext ): IndexPatternLayer | undefined { - const { timeFieldName, splitMode, splitFilters, metrics, timeInterval, dropPartialBuckets } = + const { splitMode, splitFilters, metrics, timeInterval, dropPartialBuckets, xMode, xFieldName } = layer; - const dateField = indexPattern.getFieldByName(timeFieldName!); + const xField = xFieldName ? indexPattern.getFieldByName(xFieldName) : undefined; const splitFields = layer.splitFields ? (layer.splitFields @@ -213,10 +211,10 @@ function createNewTimeseriesLayerWithMetricAggregationFromVizEditor( // generate the layer for split by terms if (splitMode === 'terms' && splitFields?.length) { - return getSplitByTermsLayer(indexPattern, splitFields, dateField, layer); + return getSplitByTermsLayer(indexPattern, splitFields, xField, xMode, layer); // generate the layer for split by filters } else if (splitMode?.includes('filter') && splitFilters && splitFilters.length) { - return getSplitByFiltersLayer(indexPattern, dateField, layer); + return getSplitByFiltersLayer(indexPattern, xField, xMode, layer); } else { const copyMetricsArray = [...metrics]; const computedLayer = computeLayerFromContext( @@ -231,11 +229,15 @@ function createNewTimeseriesLayerWithMetricAggregationFromVizEditor( return computedLayer; } + if (!xField || !xMode) { + return computedLayer; + } + return insertNewColumn({ - op: 'date_histogram', + op: xMode, layer: computedLayer, columnId: generateId(), - field: dateField, + field: xField, indexPattern, visualizationGroups: [], columnParams: { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts index 3a85a317eec0f..80f6eaecd3a8a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts @@ -148,7 +148,8 @@ describe('loader', () => { layers: [ { indexPatternId: '1', - timeFieldName: 'timestamp', + xFieldName: 'timestamp', + xMode: 'date_histogram', chartType: 'area', axisPosition: 'left', metrics: [], diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.tsx index c4ab33c36f1f1..95021dd22900f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.tsx @@ -32,29 +32,6 @@ export const FilterPopover = ({ }) => { const inputRef = React.useRef(); - // The following code is to prevent an keypress - // from propagating. - // - // TODO - It looks like EUI should be handling this - // (see https://github.com/elastic/eui/commit/ad97583b0d644690379f72c7a20879cfadb16e7a) - const popoverRef = React.useRef(null); - let panelElement: HTMLDivElement; - const panelRefCallback = (element: HTMLDivElement) => { - const listener = (event: KeyboardEvent) => { - if (event.key === 'Escape') { - event.stopPropagation(); - panelElement.removeEventListener('keydown', listener); - popoverRef.current?.closePopover(); - } - }; - - if (element) { - panelElement = element; - panelElement.addEventListener('keydown', listener); - } - }; - // End handling code - const setFilterLabel = (label: string) => setFilter({ ...filter, label }); const setFilterQuery = (input: Query) => setFilter({ ...filter, input }); @@ -70,14 +47,12 @@ export const FilterPopover = ({ return ( triggerClose()} + closePopover={triggerClose} button={button} > triggerClose()} + onSubmit={triggerClose} dataTestSubj="indexPattern-filters-label" /> diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts index 9a5abf8b79633..4a4364be1835d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts @@ -325,6 +325,7 @@ interface BaseOperationDefinitionProps< fixAction?: { label: string; newState: ( + data: DataPublicPluginStart, core: CoreStart, frame: FrameDatasourceAPI, layerId: string @@ -552,6 +553,7 @@ interface FieldBasedOperationDefinition undefined, - }, - http: { - post: jest.fn(() => - Promise.resolve({ - topValues: { - buckets: [ - { - key: 'A', - }, - { - key: 'B', - }, - ], +jest.mock('@kbn/unified-field-list-plugin/public/services/field_stats', () => ({ + loadFieldStats: jest.fn().mockResolvedValue({ + topValues: { + buckets: [ + { + key: 'A', }, - }) - ), - }, -} as unknown as CoreStart; + { + key: 'B', + }, + ], + }, + }), +})); + +const indexPattern = createMockedIndexPattern(); +const dataMock = dataPluginMock.createStartContract(); +const coreMock = corePluginMock.createStart(); function getStringBasedOperationColumn( field = 'source', @@ -214,6 +210,7 @@ describe('getDisallowedTermsMessage()', () => { indexPattern )!.fixAction.newState; const newLayer = await fixAction( + dataMock, coreMock, { query: { language: 'kuery', query: 'a: b' }, @@ -261,6 +258,7 @@ describe('getDisallowedTermsMessage()', () => { indexPattern )!.fixAction.newState; const newLayer = await fixAction( + dataMock, coreMock, { query: { language: 'kuery', query: 'a: b' }, @@ -302,6 +300,7 @@ describe('getDisallowedTermsMessage()', () => { indexPattern )!.fixAction.newState; const newLayer = await fixAction( + dataMock, coreMock, { query: { language: 'kuery', query: 'a: b' }, @@ -342,6 +341,7 @@ describe('getDisallowedTermsMessage()', () => { indexPattern )!.fixAction.newState; const newLayer = await fixAction( + dataMock, coreMock, { query: { language: 'kuery', query: 'a: b' }, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.ts index a290c2d239a1e..6dec5e3aef6cf 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.ts @@ -9,12 +9,12 @@ import { i18n } from '@kbn/i18n'; import { uniq } from 'lodash'; import type { CoreStart } from '@kbn/core/public'; import { buildEsQuery } from '@kbn/es-query'; -import { getEsQueryConfig } from '@kbn/data-plugin/public'; +import { getEsQueryConfig, DataPublicPluginStart } from '@kbn/data-plugin/public'; +import { FieldStatsResponse, loadFieldStats } from '@kbn/unified-field-list-plugin/public'; import { GenericIndexPatternColumn, operationDefinitionMap } from '..'; import { defaultLabel } from '../filters'; import { isReferenced } from '../../layer_helpers'; -import type { FieldStatsResponse } from '../../../../../common'; import type { FrameDatasourceAPI, IndexPattern, IndexPatternField } from '../../../../types'; import type { FiltersIndexPatternColumn } from '..'; import type { TermsIndexPatternColumn } from './types'; @@ -109,7 +109,12 @@ export function getDisallowedTermsMessage( label: i18n.translate('xpack.lens.indexPattern.termsWithMultipleShiftsFixActionLabel', { defaultMessage: 'Use filters', }), - newState: async (core: CoreStart, frame: FrameDatasourceAPI, layerId: string) => { + newState: async ( + data: DataPublicPluginStart, + core: CoreStart, + frame: FrameDatasourceAPI, + layerId: string + ) => { const currentColumn = layer.columns[columnId] as TermsIndexPatternColumn; const fieldNames = [ currentColumn.sourceField, @@ -133,23 +138,21 @@ export function getDisallowedTermsMessage( ); if (!activeDataFieldNameMatch || currentTerms.length === 0) { if (fieldNames.length === 1) { - const response: FieldStatsResponse = await core.http.post( - `/api/lens/index_stats/${indexPattern.id}/field`, - { - body: JSON.stringify({ - fieldName: fieldNames[0], - dslQuery: buildEsQuery( - indexPattern, - frame.query, - frame.filters, - getEsQueryConfig(core.uiSettings) - ), - fromDate: frame.dateRange.fromDate, - toDate: frame.dateRange.toDate, - size: currentColumn.params.size, - }), - } - ); + const currentDataView = await data.dataViews.get(indexPattern.id); + const response: FieldStatsResponse = await loadFieldStats({ + services: { data }, + dataView: currentDataView, + field: indexPattern.getFieldByName(fieldNames[0])!, + dslQuery: buildEsQuery( + indexPattern, + frame.query, + frame.filters, + getEsQueryConfig(core.uiSettings) + ), + fromDate: frame.dateRange.fromDate, + toDate: frame.dateRange.toDate, + size: currentColumn.params.size, + }); currentTerms = response.topValues?.buckets.map(({ key }) => String(key)) || []; } } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx index 407abc0203493..8c3b7f90d57d3 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx @@ -9,17 +9,13 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import { shallow, mount } from 'enzyme'; import { EuiButtonGroup, EuiComboBox, EuiFieldNumber, EuiSelect, EuiSwitch } from '@elastic/eui'; -import type { - IUiSettingsClient, - SavedObjectsClientContract, - HttpSetup, - CoreStart, -} from '@kbn/core/public'; +import type { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from '@kbn/core/public'; import { fieldFormatsServiceMock } from '@kbn/field-formats-plugin/public/mocks'; import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; +import { coreMock as corePluginMock } from '@kbn/core/public/mocks'; import { createMockedIndexPattern } from '../../../mocks'; import { ValuesInput } from './values_input'; import type { TermsIndexPatternColumn } from '.'; @@ -39,6 +35,21 @@ import { IndexPattern } from '../../../../types'; import { cloneDeep } from 'lodash'; import { IncludeExcludeRow } from './include_exclude_options'; +jest.mock('@kbn/unified-field-list-plugin/public/services/field_stats', () => ({ + loadFieldStats: jest.fn().mockResolvedValue({ + topValues: { + buckets: [ + { + key: 'A', + }, + { + key: 'B', + }, + ], + }, + }), +})); + // mocking random id generator function jest.mock('@elastic/eui', () => { const original = jest.requireActual('@elastic/eui'); @@ -2772,29 +2783,10 @@ describe('terms', () => { const fixAction = ( typeof errorMessage === 'object' ? errorMessage.fixAction!.newState : undefined )!; - const coreMock = { - uiSettings: { - get: () => undefined, - }, - http: { - post: jest.fn(() => - Promise.resolve({ - topValues: { - buckets: [ - { - key: 'A', - }, - { - key: 'B', - }, - ], - }, - }) - ), - }, - } as unknown as CoreStart; + const dataMock = dataPluginMock.createStartContract(); const newLayer = await fixAction( - coreMock, + dataMock, + corePluginMock.createStart(), { query: { language: 'kuery', query: 'a: b' }, filters: [], diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts index a865a1de16a77..9006cf0f2cc9c 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts @@ -39,6 +39,9 @@ import { import { TinymathAST } from '@kbn/tinymath'; import { CoreStart } from '@kbn/core/public'; import { IndexPattern } from '../../types'; +import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; + +const dataMock = dataPluginMock.createStartContract(); jest.mock('.'); jest.mock('../../id_generator'); @@ -3043,7 +3046,8 @@ describe('state_helpers', () => { indexPattern, {}, '1', - {} + {}, + dataMock ); expect(mock).toHaveBeenCalled(); expect(errors).toHaveLength(1); @@ -3069,7 +3073,8 @@ describe('state_helpers', () => { indexPattern, {} as IndexPatternPrivateState, '1', - {} as CoreStart + {} as CoreStart, + dataMock ); expect(mock).toHaveBeenCalled(); expect(errors).toHaveLength(1); @@ -3104,7 +3109,8 @@ describe('state_helpers', () => { indexPattern, {} as IndexPatternPrivateState, '1', - {} as CoreStart + {} as CoreStart, + dataMock ); expect(notCalledMock).not.toHaveBeenCalled(); expect(mock).toHaveBeenCalledTimes(1); @@ -3140,7 +3146,8 @@ describe('state_helpers', () => { indexPattern, {} as IndexPatternPrivateState, '1', - {} as CoreStart + {} as CoreStart, + dataMock ); expect(savedRef).toHaveBeenCalled(); expect(incompleteRef).not.toHaveBeenCalled(); @@ -3169,7 +3176,8 @@ describe('state_helpers', () => { indexPattern, {} as IndexPatternPrivateState, '1', - {} as CoreStart + {} as CoreStart, + dataMock ); expect(mock).toHaveBeenCalledWith( { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts index 3d079584e32f9..04860408b822e 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts @@ -9,6 +9,7 @@ import { partition, mapValues, pickBy, isArray } from 'lodash'; import { CoreStart } from '@kbn/core/public'; import type { Query } from '@kbn/es-query'; import memoizeOne from 'memoize-one'; +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { VisualizeEditorLayersContext } from '@kbn/visualizations-plugin/public'; import type { DatasourceFixAction, @@ -1375,7 +1376,8 @@ export function getErrorMessages( indexPattern: IndexPattern, state: IndexPatternPrivateState, layerId: string, - core: CoreStart + core: CoreStart, + data: DataPublicPluginStart ): | Array< | string @@ -1417,7 +1419,7 @@ export function getErrorMessages( ...state, layers: { ...state.layers, - [layerId]: await errorMessage.fixAction!.newState(core, frame, layerId), + [layerId]: await errorMessage.fixAction!.newState(data, core, frame, layerId), }, }), } @@ -1651,7 +1653,9 @@ export function computeLayerFromContext( FormulaIndexPatternColumn, 'managedReference' >; - const tempLayer = { indexPatternId: indexPattern.id, columns: {}, columnOrder: [] }; + const tempLayer = isLast + ? { indexPatternId: indexPattern.id, columns: {}, columnOrder: [] } + : computeLayerFromContext(metricsArray.length === 1, metricsArray, indexPattern); let newColumn = operationDefinition.buildColumn({ indexPattern, layer: tempLayer, @@ -1740,7 +1744,8 @@ export function computeLayerFromContext( export function getSplitByTermsLayer( indexPattern: IndexPattern, splitFields: IndexPatternField[], - dateField: IndexPatternField | undefined, + xField: IndexPatternField | undefined, + xMode: string | undefined, layer: VisualizeEditorLayersContext ): IndexPatternLayer { const { termsParams, metrics, timeInterval, splitWithDateHistogram, dropPartialBuckets } = layer; @@ -1759,18 +1764,21 @@ export function getSplitByTermsLayer( let termsLayer = insertNewColumn({ op: splitWithDateHistogram ? 'date_histogram' : 'terms', - layer: insertNewColumn({ - op: 'date_histogram', - layer: computedLayer, - columnId: generateId(), - field: dateField, - indexPattern, - visualizationGroups: [], - columnParams: { - interval: timeInterval, - dropPartials: dropPartialBuckets, - }, - }), + layer: + xField && xMode + ? insertNewColumn({ + op: xMode, + layer: computedLayer, + columnId: generateId(), + field: xField, + indexPattern, + visualizationGroups: [], + columnParams: { + interval: timeInterval, + dropPartials: dropPartialBuckets, + }, + }) + : computedLayer, columnId, field: baseField, indexPattern, @@ -1821,7 +1829,8 @@ export function getSplitByTermsLayer( export function getSplitByFiltersLayer( indexPattern: IndexPattern, - dateField: IndexPatternField | undefined, + xField: IndexPatternField | undefined, + xMode: string | undefined, layer: VisualizeEditorLayersContext ): IndexPatternLayer { const { splitFilters, metrics, timeInterval, dropPartialBuckets } = layer; @@ -1847,18 +1856,21 @@ export function getSplitByFiltersLayer( const columnId = generateId(); let filtersLayer = insertNewColumn({ op: 'filters', - layer: insertNewColumn({ - op: 'date_histogram', - layer: computedLayer, - columnId: generateId(), - field: dateField, - indexPattern, - visualizationGroups: [], - columnParams: { - interval: timeInterval, - dropPartials: dropPartialBuckets, - }, - }), + layer: + xField && xMode + ? insertNewColumn({ + op: xMode, + layer: computedLayer, + columnId: generateId(), + field: xField, + indexPattern, + visualizationGroups: [], + columnParams: { + interval: timeInterval, + dropPartials: dropPartialBuckets, + }, + }) + : computedLayer, columnId, field: undefined, indexPattern, diff --git a/x-pack/plugins/lens/public/mocks/services_mock.tsx b/x-pack/plugins/lens/public/mocks/services_mock.tsx index e9d0c7cd14f28..8c323e7e071fa 100644 --- a/x-pack/plugins/lens/public/mocks/services_mock.tsx +++ b/x-pack/plugins/lens/public/mocks/services_mock.tsx @@ -16,6 +16,7 @@ import { inspectorPluginMock } from '@kbn/inspector-plugin/public/mocks'; import { spacesPluginMock } from '@kbn/spaces-plugin/public/mocks'; import { dashboardPluginMock } from '@kbn/dashboard-plugin/public/mocks'; import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; +import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { @@ -158,6 +159,7 @@ export function makeDefaultServices( clear: jest.fn(), }, spaces: spacesPluginMock.createStartContract(), + charts: chartPluginMock.createSetupContract(), dataViewFieldEditor: indexPatternFieldEditorPluginMock.createStartContract(), dataViewEditor: indexPatternEditorPluginMock.createStartContract(), }; diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 0a76ceec5d315..97df24648f4d4 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -44,8 +44,9 @@ import { LENS_EDIT_PAGESIZE_ACTION, } from './visualizations/datatable/components/constants'; import type { LensInspector } from './lens_inspector_service'; -import { DataViewsState } from './state_management/types'; -import { IndexPatternServiceAPI } from './indexpattern_service/service'; +import type { FormatSelectorOptions } from './indexpattern_datasource/dimension_panel/format_selector'; +import type { DataViewsState } from './state_management/types'; +import type { IndexPatternServiceAPI } from './indexpattern_service/service'; export interface IndexPatternRef { id: string; @@ -228,6 +229,9 @@ interface ChartSettings { fill?: string; legend?: Record; gridLinesVisibility?: Record; + tickLabelsVisibility?: Record; + axisTitlesVisibility?: Record; + valueLabels?: boolean; extents?: { yLeftExtent: AxisExtents; yRightExtent: AxisExtents; @@ -538,7 +542,8 @@ export type DatasourceDimensionEditorProps = DatasourceDimensionPro layerType: LayerType | undefined; supportStaticValue: boolean; paramEditorCustomProps?: ParamEditorCustomProps; - supportFieldFormat?: boolean; + enableFormatSelector: boolean; + formatSelectorOptions: FormatSelectorOptions | undefined; }; export type DatasourceDimensionTriggerProps = DatasourceDimensionProps; @@ -698,7 +703,8 @@ export type VisualizationDimensionGroupConfig = SharedDimensionProps & { requiresPreviousColumnOnDuplicate?: boolean; supportStaticValue?: boolean; paramEditorCustomProps?: ParamEditorCustomProps; - supportFieldFormat?: boolean; + enableFormatSelector?: boolean; + formatSelectorOptions?: FormatSelectorOptions; // only relevant if supportFieldFormat is true labels?: { buttonAriaLabel: string; buttonLabel: string }; }; diff --git a/x-pack/plugins/lens/public/visualizations/gauge/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/gauge/visualization.test.ts index 7282259108a92..94c9ce28987bf 100644 --- a/x-pack/plugins/lens/public/visualizations/gauge/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/gauge/visualization.test.ts @@ -108,7 +108,7 @@ describe('gauge', () => { required: true, dataTestSubj: 'lnsGauge_metricDimensionPanel', enableDimensionEditor: true, - supportFieldFormat: true, + enableFormatSelector: true, }, { layerId: 'first', @@ -124,7 +124,7 @@ describe('gauge', () => { dataTestSubj: 'lnsGauge_minDimensionPanel', prioritizedOperation: 'min', suggestedValue: expect.any(Function), - supportFieldFormat: false, + enableFormatSelector: false, supportStaticValue: true, }, { @@ -141,7 +141,7 @@ describe('gauge', () => { dataTestSubj: 'lnsGauge_maxDimensionPanel', prioritizedOperation: 'max', suggestedValue: expect.any(Function), - supportFieldFormat: false, + enableFormatSelector: false, supportStaticValue: true, }, { @@ -157,7 +157,7 @@ describe('gauge', () => { supportsMoreColumns: false, required: false, dataTestSubj: 'lnsGauge_goalDimensionPanel', - supportFieldFormat: false, + enableFormatSelector: false, supportStaticValue: true, }, ], @@ -190,7 +190,7 @@ describe('gauge', () => { required: true, dataTestSubj: 'lnsGauge_metricDimensionPanel', enableDimensionEditor: true, - supportFieldFormat: true, + enableFormatSelector: true, }, { layerId: 'first', @@ -206,7 +206,7 @@ describe('gauge', () => { dataTestSubj: 'lnsGauge_minDimensionPanel', prioritizedOperation: 'min', suggestedValue: expect.any(Function), - supportFieldFormat: false, + enableFormatSelector: false, supportStaticValue: true, }, { @@ -223,7 +223,7 @@ describe('gauge', () => { dataTestSubj: 'lnsGauge_maxDimensionPanel', prioritizedOperation: 'max', suggestedValue: expect.any(Function), - supportFieldFormat: false, + enableFormatSelector: false, supportStaticValue: true, }, { @@ -239,7 +239,7 @@ describe('gauge', () => { supportsMoreColumns: true, required: false, dataTestSubj: 'lnsGauge_goalDimensionPanel', - supportFieldFormat: false, + enableFormatSelector: false, supportStaticValue: true, }, ], @@ -278,7 +278,7 @@ describe('gauge', () => { required: true, dataTestSubj: 'lnsGauge_metricDimensionPanel', enableDimensionEditor: true, - supportFieldFormat: true, + enableFormatSelector: true, }, { layerId: 'first', @@ -294,7 +294,7 @@ describe('gauge', () => { dataTestSubj: 'lnsGauge_minDimensionPanel', prioritizedOperation: 'min', suggestedValue: expect.any(Function), - supportFieldFormat: false, + enableFormatSelector: false, supportStaticValue: true, }, { @@ -311,7 +311,7 @@ describe('gauge', () => { dataTestSubj: 'lnsGauge_maxDimensionPanel', prioritizedOperation: 'max', suggestedValue: expect.any(Function), - supportFieldFormat: false, + enableFormatSelector: false, supportStaticValue: true, }, { @@ -327,7 +327,7 @@ describe('gauge', () => { supportsMoreColumns: false, required: false, dataTestSubj: 'lnsGauge_goalDimensionPanel', - supportFieldFormat: false, + enableFormatSelector: false, supportStaticValue: true, }, ], @@ -371,7 +371,7 @@ describe('gauge', () => { required: true, dataTestSubj: 'lnsGauge_metricDimensionPanel', enableDimensionEditor: true, - supportFieldFormat: true, + enableFormatSelector: true, }, { layerId: 'first', @@ -387,7 +387,7 @@ describe('gauge', () => { dataTestSubj: 'lnsGauge_minDimensionPanel', prioritizedOperation: 'min', suggestedValue: expect.any(Function), - supportFieldFormat: false, + enableFormatSelector: false, supportStaticValue: true, invalid: true, invalidMessage: 'Minimum value may not be greater than maximum value', @@ -406,7 +406,7 @@ describe('gauge', () => { dataTestSubj: 'lnsGauge_maxDimensionPanel', prioritizedOperation: 'max', suggestedValue: expect.any(Function), - supportFieldFormat: false, + enableFormatSelector: false, supportStaticValue: true, invalid: true, invalidMessage: 'Minimum value may not be greater than maximum value', @@ -424,7 +424,7 @@ describe('gauge', () => { supportsMoreColumns: false, required: false, dataTestSubj: 'lnsGauge_goalDimensionPanel', - supportFieldFormat: false, + enableFormatSelector: false, supportStaticValue: true, }, ], diff --git a/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx b/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx index 8c35a44242955..37d10918d4129 100644 --- a/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/gauge/visualization.tsx @@ -248,7 +248,7 @@ export const getGaugeVisualization = ({ return { groups: [ { - supportFieldFormat: true, + enableFormatSelector: true, layerId: state.layerId, groupId: GROUP_ID.METRIC, groupLabel: i18n.translate('xpack.lens.gauge.metricLabel', { @@ -281,7 +281,7 @@ export const getGaugeVisualization = ({ }, { supportStaticValue: true, - supportFieldFormat: false, + enableFormatSelector: false, layerId: state.layerId, groupId: GROUP_ID.MIN, groupLabel: i18n.translate('xpack.lens.gauge.minValueLabel', { @@ -307,7 +307,7 @@ export const getGaugeVisualization = ({ }, { supportStaticValue: true, - supportFieldFormat: false, + enableFormatSelector: false, layerId: state.layerId, groupId: GROUP_ID.MAX, groupLabel: i18n.translate('xpack.lens.gauge.maxValueLabel', { @@ -333,7 +333,7 @@ export const getGaugeVisualization = ({ }, { supportStaticValue: true, - supportFieldFormat: false, + enableFormatSelector: false, layerId: state.layerId, groupId: GROUP_ID.GOAL, groupLabel: i18n.translate('xpack.lens.gauge.goalValueLabel', { diff --git a/x-pack/plugins/lens/public/visualizations/legacy_metric/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/legacy_metric/dimension_editor.tsx index 9dd478712d5d9..3771adf7299e1 100644 --- a/x-pack/plugins/lens/public/visualizations/legacy_metric/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/legacy_metric/dimension_editor.tsx @@ -190,6 +190,7 @@ export function MetricDimensionEditor( palettes={props.paletteService} activePalette={activePalette} dataBounds={currentMinMax} + displayInfinity={true} setPalette={(newPalette) => { // if the new palette is not custom, replace the rangeMin with the artificial one if ( diff --git a/x-pack/plugins/lens/public/visualizations/metric/__snapshots__/visualization.test.ts.snap b/x-pack/plugins/lens/public/visualizations/metric/__snapshots__/visualization.test.ts.snap index c6fced0eba5e4..239baf2244da8 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/__snapshots__/visualization.test.ts.snap +++ b/x-pack/plugins/lens/public/visualizations/metric/__snapshots__/visualization.test.ts.snap @@ -13,7 +13,11 @@ Object { ], "dataTestSubj": "lnsMetric_primaryMetricDimensionPanel", "enableDimensionEditor": true, + "enableFormatSelector": true, "filterOperations": [Function], + "formatSelectorOptions": Object { + "disableExtraOptions": true, + }, "groupId": "metric", "groupLabel": "Primary metric", "layerId": "first", @@ -21,7 +25,6 @@ Object { "headingLabel": "Value", }, "required": true, - "supportFieldFormat": false, "supportsMoreColumns": false, }, Object { @@ -32,7 +35,11 @@ Object { ], "dataTestSubj": "lnsMetric_secondaryMetricDimensionPanel", "enableDimensionEditor": true, + "enableFormatSelector": true, "filterOperations": [Function], + "formatSelectorOptions": Object { + "disableExtraOptions": true, + }, "groupId": "secondaryMetric", "groupLabel": "Secondary metric", "layerId": "first", @@ -40,7 +47,6 @@ Object { "headingLabel": "Value", }, "required": false, - "supportFieldFormat": false, "supportsMoreColumns": false, }, Object { @@ -51,7 +57,11 @@ Object { ], "dataTestSubj": "lnsMetric_maxDimensionPanel", "enableDimensionEditor": true, + "enableFormatSelector": false, "filterOperations": [Function], + "formatSelectorOptions": Object { + "disableExtraOptions": true, + }, "groupId": "max", "groupLabel": "Maximum value", "groupTooltip": "If the maximum value is specified, the minimum value is fixed at zero.", @@ -60,7 +70,6 @@ Object { "headingLabel": "Value", }, "required": false, - "supportFieldFormat": false, "supportStaticValue": true, "supportsMoreColumns": false, }, @@ -73,12 +82,15 @@ Object { ], "dataTestSubj": "lnsMetric_breakdownByDimensionPanel", "enableDimensionEditor": true, + "enableFormatSelector": true, "filterOperations": [Function], + "formatSelectorOptions": Object { + "disableExtraOptions": true, + }, "groupId": "breakdownBy", "groupLabel": "Break down by", "layerId": "first", "required": false, - "supportFieldFormat": false, "supportsMoreColumns": false, }, ], diff --git a/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.tsx index 4439b22cd705f..de285da29b1e3 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.tsx @@ -400,6 +400,7 @@ function PrimaryMetricEditor(props: SubProps) { palettes={props.paletteService} activePalette={activePalette} dataBounds={currentMinMax} + displayInfinity={true} showRangeTypeSelector={Boolean( state.breakdownByAccessor || state.maxAccessor || diff --git a/x-pack/plugins/lens/public/visualizations/metric/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/metric/visualization.test.ts index 91a7e0ffc2053..18e664ce4ea36 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/metric/visualization.test.ts @@ -517,12 +517,8 @@ describe('metric visualization', () => { it('clears a layer', () => { expect(visualization.clearLayer(fullState, 'some-id')).toMatchInlineSnapshot(` Object { - "color": "static-color", "layerId": "first", "layerType": "data", - "maxCols": 5, - "progressDirection": "vertical", - "subtitle": "subtitle", } `); }); @@ -634,6 +630,7 @@ describe('metric visualization', () => { expect(removed).not.toHaveProperty('metricAccessor'); expect(removed).not.toHaveProperty('palette'); + expect(removed).not.toHaveProperty('color'); }); it('removes secondary metric dimension', () => { const removed = visualization.removeDimension({ @@ -651,6 +648,7 @@ describe('metric visualization', () => { }); expect(removed).not.toHaveProperty('maxAccessor'); + expect(removed).not.toHaveProperty('progressDirection'); }); it('removes breakdown-by dimension', () => { const removed = visualization.removeDimension({ @@ -660,6 +658,7 @@ describe('metric visualization', () => { expect(removed).not.toHaveProperty('breakdownByAccessor'); expect(removed).not.toHaveProperty('collapseFn'); + expect(removed).not.toHaveProperty('maxCols'); }); }); diff --git a/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx b/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx index cb1d5a53c8e3c..6d74b02bae120 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx @@ -25,6 +25,7 @@ import { GROUP_ID, LENS_METRIC_ID } from './constants'; import { DimensionEditor } from './dimension_editor'; import { Toolbar } from './toolbar'; import { generateId } from '../../id_generator'; +import { FormatSelectorOptions } from '../../indexpattern_datasource/dimension_panel/format_selector'; export const DEFAULT_MAX_COLUMNS = 3; @@ -155,6 +156,28 @@ const metricGroupLabel = i18n.translate('xpack.lens.metric.groupLabel', { defaultMessage: 'Goal and single value', }); +const removeMetricDimension = (state: MetricVisualizationState) => { + delete state.metricAccessor; + delete state.palette; + delete state.color; +}; + +const removeSecondaryMetricDimension = (state: MetricVisualizationState) => { + delete state.secondaryMetricAccessor; + delete state.secondaryPrefix; +}; + +const removeMaxDimension = (state: MetricVisualizationState) => { + delete state.maxAccessor; + delete state.progressDirection; +}; + +const removeBreakdownByDimension = (state: MetricVisualizationState) => { + delete state.breakdownByAccessor; + delete state.collapseFn; + delete state.maxCols; +}; + export const getMetricVisualization = ({ paletteService, theme, @@ -181,14 +204,13 @@ export const getMetricVisualization = ({ clearLayer(state) { const newState = { ...state }; - delete newState.metricAccessor; - delete newState.secondaryMetricAccessor; - delete newState.secondaryPrefix; - delete newState.breakdownByAccessor; - delete newState.collapseFn; - delete newState.maxAccessor; - delete newState.palette; - // TODO - clear more? + delete newState.subtitle; + + removeMetricDimension(newState); + removeSecondaryMetricDimension(newState); + removeMaxDimension(newState); + removeBreakdownByDimension(newState); + return newState; }, @@ -245,6 +267,10 @@ export const getMetricVisualization = ({ const isBucketed = (op: OperationMetadata) => op.isBucketed; + const formatterOptions: FormatSelectorOptions = { + disableExtraOptions: true, + }; + return { groups: [ { @@ -270,7 +296,8 @@ export const getMetricVisualization = ({ supportsMoreColumns: !props.state.metricAccessor, filterOperations: isSupportedDynamicMetric, enableDimensionEditor: true, - supportFieldFormat: false, + enableFormatSelector: true, + formatSelectorOptions: formatterOptions, required: true, }, { @@ -295,7 +322,8 @@ export const getMetricVisualization = ({ supportsMoreColumns: !props.state.secondaryMetricAccessor, filterOperations: isSupportedDynamicMetric, enableDimensionEditor: true, - supportFieldFormat: false, + enableFormatSelector: true, + formatSelectorOptions: formatterOptions, required: false, }, { @@ -318,7 +346,8 @@ export const getMetricVisualization = ({ supportsMoreColumns: !props.state.maxAccessor, filterOperations: isSupportedMetric, enableDimensionEditor: true, - supportFieldFormat: false, + enableFormatSelector: false, + formatSelectorOptions: formatterOptions, supportStaticValue: true, required: false, groupTooltip: i18n.translate('xpack.lens.metric.maxTooltip', { @@ -344,7 +373,8 @@ export const getMetricVisualization = ({ supportsMoreColumns: !props.state.breakdownByAccessor, filterOperations: isBucketed, enableDimensionEditor: true, - supportFieldFormat: false, + enableFormatSelector: true, + formatSelectorOptions: formatterOptions, required: false, }, ], @@ -405,20 +435,16 @@ export const getMetricVisualization = ({ const updated = { ...prevState }; if (prevState.metricAccessor === columnId) { - delete updated.metricAccessor; - delete updated.palette; - delete updated.color; + removeMetricDimension(updated); } if (prevState.secondaryMetricAccessor === columnId) { - delete updated.secondaryMetricAccessor; - delete updated.secondaryPrefix; + removeSecondaryMetricDimension(updated); } if (prevState.maxAccessor === columnId) { - delete updated.maxAccessor; + removeMaxDimension(updated); } if (prevState.breakdownByAccessor === columnId) { - delete updated.breakdownByAccessor; - delete updated.collapseFn; + removeBreakdownByDimension(updated); } return updated; diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts index 6817046864783..ad26ea92762bf 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.test.ts @@ -971,7 +971,8 @@ describe('xy_visualization', () => { layers: [ { indexPatternId: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', - timeFieldName: 'order_date', + xFieldName: 'order_date', + xMode: 'date_histogram', chartType: 'area', axisPosition: 'left', palette: { diff --git a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx index e0d0a19cebec8..24710c3261cdb 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/visualization.tsx @@ -452,7 +452,10 @@ export const getXyVisualization = ({ yLeftExtent: context.configuration.extents?.yLeftExtent, legend: context.configuration.legend, gridlinesVisibilitySettings: context.configuration.gridLinesVisibility, + tickLabelsVisibilitySettings: context.configuration.tickLabelsVisibility, + axisTitlesVisibilitySettings: context.configuration.axisTitlesVisibility, valuesInLegend: true, + valueLabels: context.configuration.valueLabels ? 'show' : 'hide', layers: visualizationStateLayers, }, }; diff --git a/x-pack/plugins/lens/server/routes/index.ts b/x-pack/plugins/lens/server/routes/index.ts index bb6986cd7d2d2..c8145cd54b9a0 100644 --- a/x-pack/plugins/lens/server/routes/index.ts +++ b/x-pack/plugins/lens/server/routes/index.ts @@ -8,9 +8,7 @@ import { CoreSetup, Logger } from '@kbn/core/server'; import { PluginStartContract } from '../plugin'; import { existingFieldsRoute } from './existing_fields'; -import { initFieldsRoute } from './field_stats'; export function setupRoutes(setup: CoreSetup, logger: Logger) { existingFieldsRoute(setup, logger); - initFieldsRoute(setup); } diff --git a/x-pack/plugins/lens/tsconfig.json b/x-pack/plugins/lens/tsconfig.json index 9f548139a47d6..e29e0d1cb86b4 100644 --- a/x-pack/plugins/lens/tsconfig.json +++ b/x-pack/plugins/lens/tsconfig.json @@ -38,6 +38,7 @@ { "path": "../../../src/plugins/data_view_editor/tsconfig.json"}, { "path": "../../../src/plugins/event_annotation/tsconfig.json"}, { "path": "../../../src/plugins/chart_expressions/expression_xy/tsconfig.json"}, - { "path": "../../../src/plugins/unified_search/tsconfig.json" } + { "path": "../../../src/plugins/unified_search/tsconfig.json" }, + { "path": "../../../src/plugins/unified_field_list/tsconfig.json" } ] } diff --git a/x-pack/plugins/maps/common/descriptor_types/layer_descriptor_types.ts b/x-pack/plugins/maps/common/descriptor_types/layer_descriptor_types.ts index 36325c561b19a..160a34f8bcc0d 100644 --- a/x-pack/plugins/maps/common/descriptor_types/layer_descriptor_types.ts +++ b/x-pack/plugins/maps/common/descriptor_types/layer_descriptor_types.ts @@ -77,6 +77,7 @@ export type VectorLayerDescriptor = LayerDescriptor & { type: LAYER_TYPE.GEOJSON_VECTOR | LAYER_TYPE.MVT_VECTOR | LAYER_TYPE.BLENDED_VECTOR; joins?: JoinDescriptor[]; style: VectorStyleDescriptor; + disableTooltips?: boolean; }; export type HeatmapLayerDescriptor = LayerDescriptor & { diff --git a/x-pack/plugins/maps/common/telemetry/index.ts b/x-pack/plugins/maps/common/telemetry/index.ts index c5ca2b05c6b52..6c5941db23d5b 100644 --- a/x-pack/plugins/maps/common/telemetry/index.ts +++ b/x-pack/plugins/maps/common/telemetry/index.ts @@ -6,6 +6,7 @@ */ export { LayerStatsCollector } from './layer_stats_collector'; +export { MapSettingsCollector } from './map_settings_collector'; export type { EMS_BASEMAP_KEYS, JOIN_KEYS, diff --git a/x-pack/plugins/maps/public/actions/layer_actions.ts b/x-pack/plugins/maps/public/actions/layer_actions.ts index 9c2fea2a302d5..7a4377f0bfc27 100644 --- a/x-pack/plugins/maps/public/actions/layer_actions.ts +++ b/x-pack/plugins/maps/public/actions/layer_actions.ts @@ -554,6 +554,15 @@ export function updateFittableFlag(id: string, includeInFitToBounds: boolean) { }; } +export function updateDisableTooltips(id: string, disableTooltips: boolean) { + return { + type: UPDATE_LAYER_PROP, + id, + propName: 'disableTooltips', + newValue: disableTooltips, + }; +} + export function setLayerQuery(id: string, query: Query) { return (dispatch: ThunkDispatch) => { dispatch({ diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx index 032f2d0384afa..3c78bf954e258 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx @@ -98,6 +98,7 @@ export interface IVectorLayer extends ILayer { hasJoins(): boolean; showJoinEditor(): boolean; canShowTooltip(): boolean; + areTooltipsDisabled(): boolean; supportsFeatureEditing(): boolean; getLeftJoinFields(): Promise; addFeature(geometry: Geometry | Position[]): Promise; @@ -115,6 +116,7 @@ export const NO_RESULTS_ICON_AND_TOOLTIPCONTENT = { export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { protected readonly _style: VectorStyle; private readonly _joins: InnerJoin[]; + protected readonly _descriptor: VectorLayerDescriptor; static createDescriptor( options: Partial, @@ -132,6 +134,8 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { layerDescriptor.joins = []; } + layerDescriptor.disableTooltips = options.disableTooltips ?? false; + return layerDescriptor; } @@ -147,6 +151,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { source, }); this._joins = joins; + this._descriptor = AbstractVectorLayer.createDescriptor(layerDescriptor); this._style = new VectorStyle( layerDescriptor.style, source, @@ -932,10 +937,23 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { return allProperties; } + /** + * Check if there are any properties we can display in a tooltip. If `false` the "Show tooltips" switch + * is disabled in Layer settings. + * @returns {boolean} + */ canShowTooltip() { return this.getSource().hasTooltipProperties() || this.getJoins().length > 0; } + /** + * Users can toggle tooltips on hover or click in the Layer settings. Tooltips are enabled by default. + * @returns {boolean} + */ + areTooltipsDisabled(): boolean { + return this._descriptor.disableTooltips ?? false; + } + getFeatureId(feature: Feature): string | number | undefined { throw new Error('Should implement AbstractVectorLayer#getFeatureId'); } diff --git a/x-pack/plugins/maps/public/classes/layers/wizards/solution_layers/observability/create_layer_descriptor.test.ts b/x-pack/plugins/maps/public/classes/layers/wizards/solution_layers/observability/create_layer_descriptor.test.ts index 7aaea96a06aed..eff03a29ac99a 100644 --- a/x-pack/plugins/maps/public/classes/layers/wizards/solution_layers/observability/create_layer_descriptor.test.ts +++ b/x-pack/plugins/maps/public/classes/layers/wizards/solution_layers/observability/create_layer_descriptor.test.ts @@ -74,6 +74,7 @@ describe('createLayerDescriptor', () => { label: '[Performance] Duration', maxZoom: 24, minZoom: 0, + disableTooltips: false, sourceDescriptor: { id: emsWorldLayerId, tooltipProperties: ['name', 'iso2'], @@ -172,6 +173,7 @@ describe('createLayerDescriptor', () => { label: '[Performance] Duration', maxZoom: 24, minZoom: 0, + disableTooltips: false, query: { language: 'kuery', query: 'processor.event:"transaction"', diff --git a/x-pack/plugins/maps/public/classes/layers/wizards/solution_layers/security/create_layer_descriptors.test.ts b/x-pack/plugins/maps/public/classes/layers/wizards/solution_layers/security/create_layer_descriptors.test.ts index 934d5c6eee3ab..2006c3eed6c2a 100644 --- a/x-pack/plugins/maps/public/classes/layers/wizards/solution_layers/security/create_layer_descriptors.test.ts +++ b/x-pack/plugins/maps/public/classes/layers/wizards/solution_layers/security/create_layer_descriptors.test.ts @@ -42,6 +42,7 @@ describe('createLayerDescriptor', () => { label: 'apm-*-transaction* | Source Point', maxZoom: 24, minZoom: 0, + disableTooltips: false, sourceDescriptor: { applyGlobalQuery: true, applyGlobalTime: true, @@ -118,6 +119,7 @@ describe('createLayerDescriptor', () => { label: 'apm-*-transaction* | Destination point', maxZoom: 24, minZoom: 0, + disableTooltips: false, sourceDescriptor: { applyGlobalQuery: true, applyGlobalTime: true, @@ -194,6 +196,7 @@ describe('createLayerDescriptor', () => { label: 'apm-*-transaction* | Line', maxZoom: 24, minZoom: 0, + disableTooltips: false, sourceDescriptor: { applyGlobalQuery: true, applyGlobalTime: true, @@ -259,6 +262,7 @@ describe('createLayerDescriptor', () => { label: 'filebeat-* | Source Point', maxZoom: 24, minZoom: 0, + disableTooltips: false, sourceDescriptor: { applyGlobalQuery: true, applyGlobalTime: true, @@ -335,6 +339,7 @@ describe('createLayerDescriptor', () => { label: 'filebeat-* | Destination point', maxZoom: 24, minZoom: 0, + disableTooltips: false, sourceDescriptor: { applyGlobalQuery: true, applyGlobalTime: true, @@ -405,6 +410,7 @@ describe('createLayerDescriptor', () => { label: 'filebeat-* | Line', maxZoom: 24, minZoom: 0, + disableTooltips: false, sourceDescriptor: { applyGlobalQuery: true, applyGlobalTime: true, @@ -470,6 +476,7 @@ describe('createLayerDescriptor', () => { label: 'traces-apm-opbean-node | Source Point', maxZoom: 24, minZoom: 0, + disableTooltips: false, sourceDescriptor: { applyGlobalQuery: true, applyGlobalTime: true, @@ -546,6 +553,7 @@ describe('createLayerDescriptor', () => { label: 'traces-apm-opbean-node | Destination point', maxZoom: 24, minZoom: 0, + disableTooltips: false, sourceDescriptor: { applyGlobalQuery: true, applyGlobalTime: true, @@ -616,6 +624,7 @@ describe('createLayerDescriptor', () => { label: 'traces-apm-opbean-node | Line', maxZoom: 24, minZoom: 0, + disableTooltips: false, sourceDescriptor: { applyGlobalQuery: true, applyGlobalTime: true, diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/custom_icon_modal.test.tsx b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/custom_icon_modal.test.tsx index 8e21679405c07..b17eb07756f27 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/custom_icon_modal.test.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/custom_icon_modal.test.tsx @@ -10,6 +10,14 @@ import { shallow } from 'enzyme'; import { CustomIconModal } from './custom_icon_modal'; +jest.mock('../../../../../kibana_services', () => ({ + getUsageCollection: () => { + return { + reportUiCounter: () => {}, + }; + }, +})); + const defaultProps = { cutoff: 0.25, onCancel: () => {}, diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/custom_icon_modal.tsx b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/custom_icon_modal.tsx index 9898ac0cd3e93..167672ad536a0 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/custom_icon_modal.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/custom_icon_modal.tsx @@ -25,6 +25,7 @@ import { EuiSpacer, EuiToolTip, } from '@elastic/eui'; +import { METRIC_TYPE } from '@kbn/analytics'; import { i18n } from '@kbn/i18n'; import { IconPreview } from './icon_preview'; // @ts-expect-error @@ -32,6 +33,8 @@ import { getCustomIconId } from '../../symbol_utils'; // @ts-expect-error import { ValidatedRange } from '../../../../../components/validated_range'; import { CustomIcon } from '../../../../../../common/descriptor_types'; +import { APP_ID } from '../../../../../../common'; +import { getUsageCollection } from '../../../../../kibana_services'; const strings = { getAdvancedOptionsLabel: () => @@ -314,6 +317,7 @@ export class CustomIconModal extends Component { } public render() { + const usageCollector = getUsageCollection(); const { symbolId, onSave, onCancel, onDelete, title } = this.props; const { label, svg, cutoff, radius, isFileInvalid } = this.state; const isComplete = label.length !== 0 && svg.length !== 0 && !isFileInvalid; @@ -365,6 +369,11 @@ export class CustomIconModal extends Component { { + usageCollector?.reportUiCounter( + APP_ID, + METRIC_TYPE.CLICK, + 'settings_custom_icons_delete' + ); onDelete(symbolId); }} data-test-subj="mapsCustomIconForm-submit" @@ -377,6 +386,14 @@ export class CustomIconModal extends Component { { + if (!onDelete) { + // Only report events when adding a new custom icon, not when editing an existing icon + usageCollector?.reportUiCounter( + APP_ID, + METRIC_TYPE.CLICK, + 'settings_custom_icons_add' + ); + } onSave({ symbolId: symbolId ?? getCustomIconId(), label, svg, cutoff, radius }); }} data-test-subj="mapsCustomIconForm-submit" diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/index.tsx b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/index.tsx index 44336a5bbaf56..e2cb962f4dd51 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/index.tsx +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/index.tsx @@ -18,6 +18,7 @@ import { updateLayerAlpha, updateLabelsOnTop, updateFittableFlag, + updateDisableTooltips, } from '../../../actions'; import { Attribution } from '../../../../common/descriptor_types'; @@ -35,6 +36,8 @@ function mapDispatchToProps(dispatch: Dispatch) { dispatch(updateLabelsOnTop(id, areLabelsOnTop)), updateIncludeInFitToBounds: (id: string, includeInFitToBounds: boolean) => dispatch(updateFittableFlag(id, includeInFitToBounds)), + updateDisableTooltips: (id: string, DisableTooltips: boolean) => + dispatch(updateDisableTooltips(id, DisableTooltips)), }; } diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/layer_settings.tsx b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/layer_settings.tsx index 4ae95b9dc5c48..794064e09d3c6 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/layer_settings.tsx +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/layer_settings.tsx @@ -24,6 +24,7 @@ import { Attribution } from '../../../../common/descriptor_types'; import { AUTOSELECT_EMS_LOCALE, NO_EMS_LOCALE, MAX_ZOOM } from '../../../../common/constants'; import { AlphaSlider } from '../../../components/alpha_slider'; import { ILayer } from '../../../classes/layers/layer'; +import { isVectorLayer, IVectorLayer } from '../../../classes/layers/vector_layer'; import { AttributionFormRow } from './attribution_form_row'; export interface Props { @@ -37,6 +38,7 @@ export interface Props { updateAlpha: (layerId: string, alpha: number) => void; updateLabelsOnTop: (layerId: string, areLabelsOnTop: boolean) => void; updateIncludeInFitToBounds: (layerId: string, includeInFitToBounds: boolean) => void; + updateDisableTooltips: (layerId: string, disableTooltips: boolean) => void; supportsFitToBounds: boolean; } @@ -68,6 +70,10 @@ export function LayerSettings(props: Props) { props.updateLabelsOnTop(layerId, event.target.checked); }; + const onShowTooltipsChange = (event: EuiSwitchEvent) => { + props.updateDisableTooltips(layerId, !event.target.checked); + }; + const includeInFitToBoundsChange = (event: EuiSwitchEvent) => { props.updateIncludeInFitToBounds(layerId, event.target.checked); }; @@ -162,6 +168,27 @@ export function LayerSettings(props: Props) { ); }; + const renderDisableTooltips = () => { + if (!isVectorLayer(props.layer)) { + return null; + } else { + const layer = props.layer as unknown as IVectorLayer; + return ( + + + + ); + } + }; + const renderShowLocaleSelector = () => { if (!props.layer.supportsLabelLocales()) { return null; @@ -234,6 +261,7 @@ export function LayerSettings(props: Props) { {renderShowLocaleSelector()} {renderIncludeInFitToBounds()} + {renderDisableTooltips()} diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.test.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.test.tsx index 736d7810757c5..cda7a4a2739b5 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.test.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.test.tsx @@ -38,6 +38,9 @@ const mockLayer = { canShowTooltip: () => { return true; }, + areTooltipsDisabled: () => { + return false; + }, getMbTooltipLayerIds: () => { return ['foo', 'bar']; }, diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.tsx index 41a69b4d3c065..70def963d01dd 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.tsx @@ -178,13 +178,13 @@ export class TooltipControl extends Component { for (let i = 0; i < mbFeatures.length; i++) { const mbFeature = mbFeatures[i]; const layer = this._getLayerByMbLayerId(mbFeature.layer.id); - if (!layer) { - break; + if (!layer || !layer.canShowTooltip() || layer.areTooltipsDisabled()) { + continue; } const featureId = layer.getFeatureId(mbFeature); if (featureId === undefined) { - break; + continue; } const layerId = layer.getId(); let match = false; @@ -192,7 +192,7 @@ export class TooltipControl extends Component { const uniqueFeature = uniqueFeatures[j]; if (featureId === uniqueFeature.id && layerId === uniqueFeature.layerId) { match = true; - break; + continue; } } if (!match) { diff --git a/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts b/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts index 2dd45ff50a2f7..181fe6c56e1ef 100644 --- a/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts +++ b/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts @@ -52,7 +52,7 @@ import { createBasemapLayerDescriptor } from '../../../classes/layers/create_bas import { whenLicenseInitialized } from '../../../licensed_features'; import { SerializedMapState, SerializedUiState } from './types'; import { setAutoOpenLayerWizardId } from '../../../actions/ui_actions'; -import { LayerStatsCollector } from '../../../../common/telemetry'; +import { LayerStatsCollector, MapSettingsCollector } from '../../../../common/telemetry'; function setMapSettingsFromEncodedState(settings: Partial) { const decodedCustomIcons = settings.customIcons @@ -281,6 +281,8 @@ export class SavedMap { return; } + const mapSettingsStatsCollector = new MapSettingsCollector(this._attributes); + const layerStatsCollector = new LayerStatsCollector(this._attributes); const uiCounterEvents = { @@ -289,6 +291,9 @@ export class SavedMap { resolution: layerStatsCollector.getResolutionCounts(), join: layerStatsCollector.getJoinCounts(), ems_basemap: layerStatsCollector.getBasemapCounts(), + settings: { + custom_icons_count: mapSettingsStatsCollector.getCustomIconsCount(), + }, }; for (const [eventType, eventTypeMetrics] of Object.entries(uiCounterEvents)) { diff --git a/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip.tsx b/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip.tsx index d5b31b418e346..b721608d53f7e 100644 --- a/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip.tsx +++ b/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip.tsx @@ -122,11 +122,14 @@ const Tooltip: FC<{ service: ChartTooltipService }> = React.memo(({ service }) = return ( = ({ children }) => { const { setHeaderActionMenu } = useContext(MlPageControlsContext); - const portalNode = useMemo(() => createPortalNode(), []); + const portalNode = useMemo(() => createHtmlPortalNode(), []); useEffect(() => { if (!setHeaderActionMenu) { diff --git a/x-pack/plugins/ml/public/application/components/ml_page/ml_page.tsx b/x-pack/plugins/ml/public/application/components/ml_page/ml_page.tsx index 90dd95dbafb71..bd12a120420c2 100644 --- a/x-pack/plugins/ml/public/application/components/ml_page/ml_page.tsx +++ b/x-pack/plugins/ml/public/application/components/ml_page/ml_page.tsx @@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n'; import { Redirect, Route, Switch } from 'react-router-dom'; import type { AppMountParameters } from '@kbn/core/public'; import { KibanaPageTemplate, RedirectAppLinks } from '@kbn/kibana-react-plugin/public'; -import { createPortalNode, PortalNode } from 'react-reverse-portal'; +import { createHtmlPortalNode, HtmlPortalNode } from 'react-reverse-portal'; import { MlPageHeaderRenderer } from '../page_header/page_header'; import { useSideNavItems } from './side_nav'; import * as routes from '../../routing/routes'; @@ -23,13 +23,13 @@ import { useActiveRoute } from '../../routing/use_active_route'; import { useDocTitle } from '../../routing/use_doc_title'; export const MlPageControlsContext = createContext<{ - headerPortal: PortalNode; + headerPortal: HtmlPortalNode; setHeaderActionMenu?: AppMountParameters['setHeaderActionMenu']; setIsHeaderMounted: (v: boolean) => void; isHeaderMounted: boolean; }>({ setHeaderActionMenu: () => {}, - headerPortal: createPortalNode(), + headerPortal: createHtmlPortalNode(), isHeaderMounted: false, setIsHeaderMounted: () => {}, }); @@ -46,7 +46,7 @@ export const MlPage: FC<{ pageDeps: PageDependencies }> = React.memo(({ pageDeps }, } = useMlKibana(); - const headerPortalNode = useMemo(() => createPortalNode(), []); + const headerPortalNode = useMemo(() => createHtmlPortalNode(), []); const [isHeaderMounted, setIsHeaderMounted] = useState(false); const routeList = useMemo( @@ -114,7 +114,7 @@ interface CommonPageWrapperProps { setIsHeaderMounted: (v: boolean) => void; pageDeps: PageDependencies; routeList: MlRoute[]; - headerPortal: PortalNode; + headerPortal: HtmlPortalNode; } const CommonPageWrapper: FC = React.memo(({ pageDeps, routeList }) => { diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_script.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_script.json index 022695bcf5a7d..af8bd86005dd9 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_script.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/ml/v3_windows_anomalous_script.json @@ -21,7 +21,7 @@ "influencers": [ "host.name", "user.name", - "file.Path" + "file.path" ] }, "allow_lazy_open": true, diff --git a/x-pack/plugins/monitoring/public/application/hooks/use_breadcrumbs.ts b/x-pack/plugins/monitoring/public/application/hooks/use_breadcrumbs.ts index 95cae1720fd07..7654e2d734e4c 100644 --- a/x-pack/plugins/monitoring/public/application/hooks/use_breadcrumbs.ts +++ b/x-pack/plugins/monitoring/public/application/hooks/use_breadcrumbs.ts @@ -301,4 +301,4 @@ export const useBreadcrumbs = ({ history }: { history: History }) => { }; }; -export const BreadcrumbContainer = createContainer(useBreadcrumbs); +export const [BreadcrumbContainer, useBreadcrumbContainerContext] = createContainer(useBreadcrumbs); diff --git a/x-pack/plugins/monitoring/public/application/hooks/use_charts.tsx b/x-pack/plugins/monitoring/public/application/hooks/use_charts.tsx index c37fae742d0ca..f456b62312ec6 100644 --- a/x-pack/plugins/monitoring/public/application/hooks/use_charts.tsx +++ b/x-pack/plugins/monitoring/public/application/hooks/use_charts.tsx @@ -5,15 +5,15 @@ * 2.0. */ import moment from 'moment'; -import { useContext, useState, useEffect, useRef } from 'react'; +import { useState, useEffect, useRef } from 'react'; import { useHistory } from 'react-router-dom'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { MonitoringTimeContainer } from './use_monitoring_time'; +import { useMonitoringTimeContainerContext } from './use_monitoring_time'; export function useCharts() { const { services } = useKibana<{ data: any }>(); const history = useHistory(); - const { handleTimeChange } = useContext(MonitoringTimeContainer.Context); + const { handleTimeChange } = useMonitoringTimeContainerContext(); const [zoomInLevel, setZoomInLevel] = useState(0); diff --git a/x-pack/plugins/monitoring/public/application/hooks/use_monitoring_time.ts b/x-pack/plugins/monitoring/public/application/hooks/use_monitoring_time.ts index 13f40fc89a704..74807fa957e6a 100644 --- a/x-pack/plugins/monitoring/public/application/hooks/use_monitoring_time.ts +++ b/x-pack/plugins/monitoring/public/application/hooks/use_monitoring_time.ts @@ -75,4 +75,5 @@ export const useMonitoringTime = () => { }; }; -export const MonitoringTimeContainer = createContainer(useMonitoringTime); +export const [MonitoringTimeContainer, useMonitoringTimeContainerContext] = + createContainer(useMonitoringTime); diff --git a/x-pack/plugins/monitoring/public/application/index.tsx b/x-pack/plugins/monitoring/public/application/index.tsx index 3fd12c312d13f..b313992863023 100644 --- a/x-pack/plugins/monitoring/public/application/index.tsx +++ b/x-pack/plugins/monitoring/public/application/index.tsx @@ -107,8 +107,8 @@ const MonitoringApp: React.FC<{ uiSettings={core.uiSettings} > - - + + @@ -336,8 +336,8 @@ const MonitoringApp: React.FC<{ /> - - + + diff --git a/x-pack/plugins/monitoring/public/application/pages/apm/instance.tsx b/x-pack/plugins/monitoring/public/application/pages/apm/instance.tsx index 97f19f3cf9204..e4340d6fdaa67 100644 --- a/x-pack/plugins/monitoring/public/application/pages/apm/instance.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/apm/instance.tsx @@ -13,7 +13,7 @@ import { useKibana } from '@kbn/kibana-react-plugin/public'; import { ComponentProps } from '../../route_init'; import { GlobalStateContext } from '../../contexts/global_state_context'; import { useCharts } from '../../hooks/use_charts'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; import { PageTemplate } from '../page_template'; // @ts-ignore import { ApmServerInstance } from '../../../components/apm/instance'; @@ -23,7 +23,7 @@ export const ApmInstancePage: React.FC = ({ clusters }) => { const globalState = useContext(GlobalStateContext); const { services } = useKibana<{ data: any }>(); - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const { zoomInfo, onBrush } = useCharts(); const clusterUuid = globalState.cluster_uuid; const ccs = globalState.ccs; diff --git a/x-pack/plugins/monitoring/public/application/pages/apm/instances.tsx b/x-pack/plugins/monitoring/public/application/pages/apm/instances.tsx index 29f6a1f9e056e..e86ae43034af4 100644 --- a/x-pack/plugins/monitoring/public/application/pages/apm/instances.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/apm/instances.tsx @@ -17,7 +17,7 @@ import { ApmTemplate } from './apm_template'; import { ApmServerInstances } from '../../../components/apm/instances'; import { SetupModeRenderer } from '../../../components/renderers/setup_mode'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; import { APM_SYSTEM_ID } from '../../../../common/constants'; interface SetupModeProps { @@ -29,7 +29,7 @@ interface SetupModeProps { export const ApmInstancesPage: React.FC = ({ clusters }) => { const globalState = useContext(GlobalStateContext); const { services } = useKibana<{ data: any }>(); - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const { updateTotalItemCount, getPaginationTableProps } = useTable('apm.instances'); const clusterUuid = globalState.cluster_uuid; const ccs = globalState.ccs; diff --git a/x-pack/plugins/monitoring/public/application/pages/apm/overview.tsx b/x-pack/plugins/monitoring/public/application/pages/apm/overview.tsx index 1cf264daaeea3..39890954c877c 100644 --- a/x-pack/plugins/monitoring/public/application/pages/apm/overview.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/apm/overview.tsx @@ -13,7 +13,7 @@ import { ComponentProps } from '../../route_init'; import { ApmTemplate } from './apm_template'; import { GlobalStateContext } from '../../contexts/global_state_context'; import { useCharts } from '../../hooks/use_charts'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; // @ts-ignore import { ApmOverview } from '../../../components/apm/overview'; @@ -23,7 +23,7 @@ export const ApmOverviewPage: React.FC = ({ clusters }) => { const { services } = useKibana<{ data: any }>(); const clusterUuid = globalState.cluster_uuid; const ccs = globalState.ccs; - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const cluster = find(clusters, { cluster_uuid: clusterUuid, }) as any; diff --git a/x-pack/plugins/monitoring/public/application/pages/beats/instance.tsx b/x-pack/plugins/monitoring/public/application/pages/beats/instance.tsx index 68345db40caf2..f9b4794e44fed 100644 --- a/x-pack/plugins/monitoring/public/application/pages/beats/instance.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/beats/instance.tsx @@ -15,7 +15,7 @@ import { GlobalStateContext } from '../../contexts/global_state_context'; import { useCharts } from '../../hooks/use_charts'; // @ts-ignore import { Beat } from '../../../components/beats/beat'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; import { BeatsTemplate } from './beats_template'; export const BeatsInstancePage: React.FC = ({ clusters }) => { @@ -23,7 +23,7 @@ export const BeatsInstancePage: React.FC = ({ clusters }) => { const globalState = useContext(GlobalStateContext); const { services } = useKibana<{ data: any }>(); - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const { zoomInfo, onBrush } = useCharts(); const clusterUuid = globalState.cluster_uuid; const ccs = globalState.ccs; diff --git a/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx b/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx index ea4f04151e83a..6fce1668fc98a 100644 --- a/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx @@ -17,13 +17,13 @@ import { BeatsTemplate } from './beats_template'; import { Listing } from '../../../components/beats/listing'; import { SetupModeRenderer, SetupModeProps } from '../../../components/renderers/setup_mode'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; import { BEATS_SYSTEM_ID } from '../../../../common/constants'; export const BeatsInstancesPage: React.FC = ({ clusters }) => { const globalState = useContext(GlobalStateContext); const { services } = useKibana<{ data: any }>(); - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const { updateTotalItemCount, getPaginationTableProps } = useTable('beats.instances'); const clusterUuid = globalState.cluster_uuid; const ccs = globalState.ccs; diff --git a/x-pack/plugins/monitoring/public/application/pages/beats/overview.tsx b/x-pack/plugins/monitoring/public/application/pages/beats/overview.tsx index 610fbe7ce2daf..4a142a8abd425 100644 --- a/x-pack/plugins/monitoring/public/application/pages/beats/overview.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/beats/overview.tsx @@ -15,7 +15,7 @@ import { GlobalStateContext } from '../../contexts/global_state_context'; import { useCharts } from '../../hooks/use_charts'; // @ts-ignore import { BeatsOverview } from '../../../components/beats/overview'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; export const BeatsOverviewPage: React.FC = ({ clusters }) => { const globalState = useContext(GlobalStateContext); @@ -23,7 +23,7 @@ export const BeatsOverviewPage: React.FC = ({ clusters }) => { const { services } = useKibana<{ data: any }>(); const clusterUuid = globalState.cluster_uuid; const ccs = globalState.ccs; - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const cluster = find(clusters, { cluster_uuid: clusterUuid, }) as any; diff --git a/x-pack/plugins/monitoring/public/application/pages/cluster/overview_page.tsx b/x-pack/plugins/monitoring/public/application/pages/cluster/overview_page.tsx index d128beee7ce87..993cabad38af0 100644 --- a/x-pack/plugins/monitoring/public/application/pages/cluster/overview_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/cluster/overview_page.tsx @@ -16,7 +16,7 @@ import { Overview } from '../../../components/cluster/overview'; import { ExternalConfigContext } from '../../contexts/external_config_context'; import { SetupModeRenderer, SetupModeProps } from '../../../components/renderers/setup_mode'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; import { fetchClusters } from '../../../lib/fetch_clusters'; import { AlertsByName } from '../../../alerts/types'; import { fetchAlerts } from '../../../lib/fetch_alerts'; @@ -33,7 +33,7 @@ export const ClusterOverview: React.FC<{}> = () => { const [clusters, setClusters] = useState([] as any); const [alerts, setAlerts] = useState({}); const [loaded, setLoaded] = useState(false); - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); let tabs: TabMenuItem[] = []; diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_page.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_page.tsx index cc0113acb3b5f..9018d2ab2071d 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_page.tsx @@ -18,7 +18,7 @@ import { SetupModeContext } from '../../../components/setup_mode/setup_mode_cont import { AlertsByName } from '../../../alerts/types'; import { fetchAlerts } from '../../../lib/fetch_alerts'; import { ELASTICSEARCH_SYSTEM_ID, RULE_CCR_READ_EXCEPTIONS } from '../../../../common/constants'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; interface SetupModeProps { setupMode: any; flyoutComponent: any; @@ -27,7 +27,7 @@ interface SetupModeProps { export const ElasticsearchCcrPage: React.FC = ({ clusters }) => { const globalState = useContext(GlobalStateContext); - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const { services } = useKibana<{ data: any }>(); const clusterUuid = globalState.cluster_uuid; diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_shard_page.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_shard_page.tsx index 925baa5edd052..3db3c25fd1f39 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_shard_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_shard_page.tsx @@ -20,7 +20,7 @@ import { SetupModeContext } from '../../../components/setup_mode/setup_mode_cont import { AlertsByName } from '../../../alerts/types'; import { fetchAlerts } from '../../../lib/fetch_alerts'; import { ELASTICSEARCH_SYSTEM_ID, RULE_CCR_READ_EXCEPTIONS } from '../../../../common/constants'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; interface SetupModeProps { setupMode: any; @@ -33,7 +33,7 @@ export const ElasticsearchCcrShardPage: React.FC = ({ clusters } const { services } = useKibana<{ data: any }>(); const [data, setData] = useState({} as any); const { index, shardId }: { index: string; shardId: string } = useParams(); - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const clusterUuid = globalState.cluster_uuid; const cluster = find(clusters, { diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_advanced_page.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_advanced_page.tsx index 410a909a9c4ab..0dab4a4d4bc52 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_advanced_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_advanced_page.tsx @@ -20,11 +20,11 @@ import { AdvancedIndex } from '../../../components/elasticsearch/index/advanced' import { AlertsByName } from '../../../alerts/types'; import { fetchAlerts } from '../../../lib/fetch_alerts'; import { ELASTICSEARCH_SYSTEM_ID, RULE_LARGE_SHARD_SIZE } from '../../../../common/constants'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; export const ElasticsearchIndexAdvancedPage: React.FC = ({ clusters }) => { const globalState = useContext(GlobalStateContext); - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const { services } = useKibana<{ data: any }>(); const { index }: { index: string } = useParams(); const { zoomInfo, onBrush } = useCharts(); diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_page.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_page.tsx index dc2f718e64700..73c32fe0a42f3 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_page.tsx @@ -24,11 +24,11 @@ import { labels } from '../../../components/elasticsearch/shard_allocation/lib/l import { AlertsByName } from '../../../alerts/types'; import { fetchAlerts } from '../../../lib/fetch_alerts'; import { ELASTICSEARCH_SYSTEM_ID, RULE_LARGE_SHARD_SIZE } from '../../../../common/constants'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; export const ElasticsearchIndexPage: React.FC = ({ clusters }) => { const globalState = useContext(GlobalStateContext); - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const { services } = useKibana<{ data: any }>(); const { index }: { index: string } = useParams(); const { zoomInfo, onBrush } = useCharts(); diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/indices_page.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/indices_page.tsx index d14015532167e..49be26854571e 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/indices_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/indices_page.tsx @@ -19,11 +19,11 @@ import { useLocalStorage } from '../../hooks/use_local_storage'; import { AlertsByName } from '../../../alerts/types'; import { fetchAlerts } from '../../../lib/fetch_alerts'; import { ELASTICSEARCH_SYSTEM_ID, RULE_LARGE_SHARD_SIZE } from '../../../../common/constants'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; export const ElasticsearchIndicesPage: React.FC = ({ clusters }) => { const globalState = useContext(GlobalStateContext); - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const { services } = useKibana<{ data: any }>(); const { getPaginationTableProps } = useTable('elasticsearch.indices'); const clusterUuid = globalState.cluster_uuid; diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ml_jobs_page.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ml_jobs_page.tsx index 5c7fd227a0a53..0f9fe473ae4af 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ml_jobs_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ml_jobs_page.tsx @@ -16,7 +16,7 @@ import { SetupModeRenderer } from '../../../components/renderers/setup_mode'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; import { useTable } from '../../hooks/use_table'; import type { MLJobs } from '../../../types'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; import { ELASTICSEARCH_SYSTEM_ID } from '../../../../common/constants'; interface SetupModeProps { @@ -27,7 +27,7 @@ interface SetupModeProps { export const ElasticsearchMLJobsPage: React.FC = ({ clusters }) => { const globalState = useContext(GlobalStateContext); - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const { services } = useKibana<{ data: any }>(); const { getPaginationTableProps } = useTable('elasticsearch.mlJobs'); const clusterUuid = globalState.cluster_uuid; diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/node_advanced_page.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/node_advanced_page.tsx index 05394a6d36aec..bf4b6c48b551e 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/node_advanced_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/node_advanced_page.tsx @@ -25,11 +25,11 @@ import { RULE_DISK_USAGE, RULE_MEMORY_USAGE, } from '../../../../common/constants'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; export const ElasticsearchNodeAdvancedPage: React.FC = ({ clusters }) => { const globalState = useContext(GlobalStateContext); - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const { zoomInfo, onBrush } = useCharts(); const [data, setData] = useState({} as any); diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/node_page.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/node_page.tsx index e41e921985015..b6682a2da876d 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/node_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/node_page.tsx @@ -31,11 +31,11 @@ import { RULE_DISK_USAGE, RULE_MEMORY_USAGE, } from '../../../../common/constants'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; export const ElasticsearchNodePage: React.FC = ({ clusters }) => { const globalState = useContext(GlobalStateContext); - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const { zoomInfo, onBrush } = useCharts(); const [showSystemIndices, setShowSystemIndices] = useLocalStorage( 'showSystemIndices', diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/nodes_page.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/nodes_page.tsx index 156d8463b80be..b258361474a61 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/nodes_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/nodes_page.tsx @@ -16,7 +16,7 @@ import { ComponentProps } from '../../route_init'; import { SetupModeRenderer, SetupModeProps } from '../../../components/renderers/setup_mode'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; import { useTable } from '../../hooks/use_table'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; import { AlertsByName } from '../../../alerts/types'; import { fetchAlerts } from '../../../lib/fetch_alerts'; import { @@ -34,7 +34,7 @@ export const ElasticsearchNodesPage: React.FC = ({ clusters }) = const { showCgroupMetricsElasticsearch } = useContext(ExternalConfigContext); const { services } = useKibana<{ data: any }>(); const [isLoading, setIsLoading] = React.useState(false); - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const { getPaginationRouteOptions, updateTotalItemCount, getPaginationTableProps } = useTable('elasticsearch.nodes'); const clusterUuid = globalState.cluster_uuid; diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/overview.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/overview.tsx index c4524bb161040..4e213405cfd08 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/overview.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/overview.tsx @@ -14,13 +14,13 @@ import { GlobalStateContext } from '../../contexts/global_state_context'; import { ElasticsearchOverview } from '../../../components/elasticsearch'; import { ComponentProps } from '../../route_init'; import { useCharts } from '../../hooks/use_charts'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; export const ElasticsearchOverviewPage: React.FC = ({ clusters }) => { const globalState = useContext(GlobalStateContext); const { zoomInfo, onBrush } = useCharts(); const { services } = useKibana<{ data: any }>(); - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const clusterUuid = globalState.cluster_uuid; const ccs = globalState.ccs; const cluster = find(clusters, { diff --git a/x-pack/plugins/monitoring/public/application/pages/enterprise_search/overview.tsx b/x-pack/plugins/monitoring/public/application/pages/enterprise_search/overview.tsx index 75d2d2f89fae1..0cd97166dcf5d 100644 --- a/x-pack/plugins/monitoring/public/application/pages/enterprise_search/overview.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/enterprise_search/overview.tsx @@ -14,7 +14,7 @@ import { EntSearchTemplate } from './ent_search_template'; import { GlobalStateContext } from '../../contexts/global_state_context'; import { useCharts } from '../../hooks/use_charts'; import { EnterpriseSearchOverview } from '../../../components/enterprise_search/overview'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; export const EntSearchOverviewPage: React.FC = ({ clusters }) => { const globalState = useContext(GlobalStateContext); @@ -22,7 +22,7 @@ export const EntSearchOverviewPage: React.FC = ({ clusters }) => const { services } = useKibana<{ data: any }>(); const clusterUuid = globalState.cluster_uuid; const ccs = globalState.ccs; - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const cluster = find(clusters, { cluster_uuid: clusterUuid, }) as any; diff --git a/x-pack/plugins/monitoring/public/application/pages/home/cluster_listing.tsx b/x-pack/plugins/monitoring/public/application/pages/home/cluster_listing.tsx index 80c46342294f5..45ed6172d4262 100644 --- a/x-pack/plugins/monitoring/public/application/pages/home/cluster_listing.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/home/cluster_listing.tsx @@ -17,7 +17,7 @@ import { ExternalConfigContext } from '../../contexts/external_config_context'; import { ComponentProps } from '../../route_init'; import { useTable } from '../../hooks/use_table'; import { PageTemplate, TabMenuItem } from '../page_template'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; import { fetchClusters } from '../../../lib/fetch_clusters'; const pageTitle = i18n.translate('xpack.monitoring.cluster.listing.pageTitle', { @@ -40,7 +40,7 @@ export const ClusterListing: React.FC = () => { const externalConfig = useContext(ExternalConfigContext); const { services } = useKibana<{ data: any }>(); const [clusters, setClusters] = useState([] as any); - const { update: updateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { update: updateBreadcrumbs } = useBreadcrumbContainerContext(); const fakeScope = { $evalAsync: (fn: () => void) => fn(), diff --git a/x-pack/plugins/monitoring/public/application/pages/kibana/instance.tsx b/x-pack/plugins/monitoring/public/application/pages/kibana/instance.tsx index c1dfecd69d2a8..56d8b6742c282 100644 --- a/x-pack/plugins/monitoring/public/application/pages/kibana/instance.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/kibana/instance.tsx @@ -23,7 +23,7 @@ import { ComponentProps } from '../../route_init'; import { GlobalStateContext } from '../../contexts/global_state_context'; import { useCharts } from '../../hooks/use_charts'; // @ts-ignore -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; // @ts-ignore import { MonitoringTimeseriesContainer } from '../../../components/chart'; // @ts-ignore @@ -148,7 +148,7 @@ export const KibanaInstancePage: React.FC = ({ clusters }) => { const globalState = useContext(GlobalStateContext); const { services } = useKibana<{ data: any }>(); - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const clusterUuid = globalState.cluster_uuid; const ccs = globalState.ccs; const cluster = find(clusters, { diff --git a/x-pack/plugins/monitoring/public/application/pages/kibana/instances.tsx b/x-pack/plugins/monitoring/public/application/pages/kibana/instances.tsx index 6f1f822b8488f..5d272be8be30c 100644 --- a/x-pack/plugins/monitoring/public/application/pages/kibana/instances.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/kibana/instances.tsx @@ -18,7 +18,7 @@ import { KibanaInstances } from '../../../components/kibana/instances'; // @ts-ignore import { SetupModeRenderer, SetupModeProps } from '../../../components/renderers/setup_mode'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; import { AlertsByName } from '../../../alerts/types'; import { fetchAlerts } from '../../../lib/fetch_alerts'; import { KIBANA_SYSTEM_ID, RULE_KIBANA_VERSION_MISMATCH } from '../../../../common/constants'; @@ -26,7 +26,7 @@ import { KIBANA_SYSTEM_ID, RULE_KIBANA_VERSION_MISMATCH } from '../../../../comm export const KibanaInstancesPage: React.FC = ({ clusters }) => { const { cluster_uuid: clusterUuid, ccs } = useContext(GlobalStateContext); const { services } = useKibana<{ data: any }>(); - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const { updateTotalItemCount, getPaginationTableProps } = useTable('kibana.instances'); const cluster = find(clusters, { cluster_uuid: clusterUuid, diff --git a/x-pack/plugins/monitoring/public/application/pages/kibana/overview.tsx b/x-pack/plugins/monitoring/public/application/pages/kibana/overview.tsx index 5ae89fd929eb4..dd31acec85e11 100644 --- a/x-pack/plugins/monitoring/public/application/pages/kibana/overview.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/kibana/overview.tsx @@ -25,7 +25,7 @@ import { ComponentProps } from '../../route_init'; import { MonitoringTimeseriesContainer } from '../../../components/chart'; // @ts-ignore import { ClusterStatus } from '../../../components/kibana/cluster_status'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; import { useCharts } from '../../hooks/use_charts'; const KibanaOverview = ({ data }: { data: any }) => { @@ -107,7 +107,7 @@ const KibanaOverview = ({ data }: { data: any }) => { export const KibanaOverviewPage: React.FC = ({ clusters }) => { const globalState = useContext(GlobalStateContext); const { services } = useKibana<{ data: any }>(); - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const [data, setData] = useState(); const clusterUuid = globalState.cluster_uuid; const cluster = find(clusters, { diff --git a/x-pack/plugins/monitoring/public/application/pages/license_page.tsx b/x-pack/plugins/monitoring/public/application/pages/license_page.tsx index 2f0e424d14238..b7288ce1b872b 100644 --- a/x-pack/plugins/monitoring/public/application/pages/license_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/license_page.tsx @@ -13,7 +13,7 @@ import { PageTemplate } from './page_template'; import { License } from '../../components'; import { GlobalStateContext } from '../contexts/global_state_context'; import { CODE_PATH_LICENSE, STANDALONE_CLUSTER_CLUSTER_UUID } from '../../../common/constants'; -import { MonitoringTimeContainer } from '../hooks/use_monitoring_time'; +import { useMonitoringTimeContainerContext } from '../hooks/use_monitoring_time'; const CODE_PATHS = [CODE_PATH_LICENSE]; @@ -22,7 +22,7 @@ export const LicensePage: React.FC<{}> = () => { defaultMessage: 'License', }); - const { setIsDisabled } = useContext(MonitoringTimeContainer.Context); + const { setIsDisabled } = useMonitoringTimeContainerContext(); useEffect(() => { setIsDisabled(true); return () => { diff --git a/x-pack/plugins/monitoring/public/application/pages/logstash/advanced.tsx b/x-pack/plugins/monitoring/public/application/pages/logstash/advanced.tsx index 29b2e4f6c1e44..f89d7b5df3726 100644 --- a/x-pack/plugins/monitoring/public/application/pages/logstash/advanced.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/logstash/advanced.tsx @@ -30,7 +30,7 @@ import { useCharts } from '../../hooks/use_charts'; import { AlertsByName } from '../../../alerts/types'; import { fetchAlerts } from '../../../lib/fetch_alerts'; import { RULE_LOGSTASH_VERSION_MISMATCH } from '../../../../common/constants'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; export const LogStashNodeAdvancedPage: React.FC = ({ clusters }) => { const globalState = useContext(GlobalStateContext); @@ -43,7 +43,7 @@ export const LogStashNodeAdvancedPage: React.FC = ({ clusters }) cluster_uuid: clusterUuid, }) as any; - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const [data, setData] = useState({} as any); const [alerts, setAlerts] = useState({}); diff --git a/x-pack/plugins/monitoring/public/application/pages/logstash/node.tsx b/x-pack/plugins/monitoring/public/application/pages/logstash/node.tsx index 772bf5718c4ec..75d752b696fd9 100644 --- a/x-pack/plugins/monitoring/public/application/pages/logstash/node.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/logstash/node.tsx @@ -31,13 +31,13 @@ import { useCharts } from '../../hooks/use_charts'; import { AlertsByName } from '../../../alerts/types'; import { fetchAlerts } from '../../../lib/fetch_alerts'; import { RULE_LOGSTASH_VERSION_MISMATCH } from '../../../../common/constants'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; export const LogStashNodePage: React.FC = ({ clusters }) => { const match = useRouteMatch<{ uuid: string | undefined }>(); const globalState = useContext(GlobalStateContext); const { services } = useKibana<{ data: any }>(); - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const clusterUuid = globalState.cluster_uuid; const ccs = globalState.ccs; const cluster = find(clusters, { diff --git a/x-pack/plugins/monitoring/public/application/pages/logstash/node_pipelines.tsx b/x-pack/plugins/monitoring/public/application/pages/logstash/node_pipelines.tsx index 90c8421a31eb0..6c97661e915cb 100644 --- a/x-pack/plugins/monitoring/public/application/pages/logstash/node_pipelines.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/logstash/node_pipelines.tsx @@ -20,7 +20,7 @@ import { useTable } from '../../hooks/use_table'; // @ts-expect-error import { PipelineListing } from '../../../components/logstash/pipeline_listing/pipeline_listing'; import { useCharts } from '../../hooks/use_charts'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; export const LogStashNodePipelinesPage: React.FC = ({ clusters }) => { const globalState = useContext(GlobalStateContext); @@ -33,7 +33,7 @@ export const LogStashNodePipelinesPage: React.FC = ({ clusters } cluster_uuid: clusterUuid, }) as any; - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const { getPaginationTableProps, getPaginationRouteOptions, updateTotalItemCount } = useTable('logstash.pipelines'); diff --git a/x-pack/plugins/monitoring/public/application/pages/logstash/nodes.tsx b/x-pack/plugins/monitoring/public/application/pages/logstash/nodes.tsx index b350f057aae96..d3097ecc4a145 100644 --- a/x-pack/plugins/monitoring/public/application/pages/logstash/nodes.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/logstash/nodes.tsx @@ -19,7 +19,7 @@ import { useTable } from '../../hooks/use_table'; import { LOGSTASH_SYSTEM_ID, RULE_LOGSTASH_VERSION_MISMATCH } from '../../../../common/constants'; import { AlertsByName } from '../../../alerts/types'; import { fetchAlerts } from '../../../lib/fetch_alerts'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; interface SetupModeProps { setupMode: any; @@ -32,7 +32,7 @@ export const LogStashNodesPage: React.FC = ({ clusters }) => { const { services } = useKibana<{ data: any }>(); const clusterUuid = globalState.cluster_uuid; const ccs = globalState.ccs; - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const cluster = find(clusters, { cluster_uuid: clusterUuid, }) as any; diff --git a/x-pack/plugins/monitoring/public/application/pages/logstash/overview.tsx b/x-pack/plugins/monitoring/public/application/pages/logstash/overview.tsx index e8b9dda758713..20e7339e2b580 100644 --- a/x-pack/plugins/monitoring/public/application/pages/logstash/overview.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/logstash/overview.tsx @@ -14,7 +14,7 @@ import { useCharts } from '../../hooks/use_charts'; // @ts-ignore import { Overview } from '../../../components/logstash/overview'; import { LogstashTemplate } from './logstash_template'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; export const LogStashOverviewPage: React.FC = ({ clusters }) => { const globalState = useContext(GlobalStateContext); @@ -25,7 +25,7 @@ export const LogStashOverviewPage: React.FC = ({ clusters }) => const cluster = find(clusters, { cluster_uuid: clusterUuid, }) as any; - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const [data, setData] = useState(null); // const [showShardActivityHistory, setShowShardActivityHistory] = useState(false); diff --git a/x-pack/plugins/monitoring/public/application/pages/logstash/pipeline.tsx b/x-pack/plugins/monitoring/public/application/pages/logstash/pipeline.tsx index af68390aa4e76..c3c229458da08 100644 --- a/x-pack/plugins/monitoring/public/application/pages/logstash/pipeline.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/logstash/pipeline.tsx @@ -29,7 +29,7 @@ import { formatTimestampToDuration } from '../../../../common'; import { CALCULATE_DURATION_SINCE } from '../../../../common/constants'; import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link'; import { PipelineVersions } from './pipeline_versions_dropdown'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; export const LogStashPipelinePage: React.FC = ({ clusters }) => { const match = useRouteMatch<{ id: string | undefined; hash: string | undefined }>(); @@ -126,7 +126,7 @@ export const LogStashPipelinePage: React.FC = ({ clusters }) => }, [data]); const timeseriesTooltipXValueFormatter = (xValue: any) => moment(xValue).format(dateFormat); - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const onVertexChange = useCallback((vertex: any) => { if (!vertex) { diff --git a/x-pack/plugins/monitoring/public/application/pages/logstash/pipelines.tsx b/x-pack/plugins/monitoring/public/application/pages/logstash/pipelines.tsx index a669b06ac8bcf..19d5277f1a85a 100644 --- a/x-pack/plugins/monitoring/public/application/pages/logstash/pipelines.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/logstash/pipelines.tsx @@ -17,7 +17,7 @@ import { isPipelineMonitoringSupportedInVersion } from '../../../lib/logstash/pi import { PipelineListing } from '../../../components/logstash/pipeline_listing/pipeline_listing'; import { LogstashTemplate } from './logstash_template'; import { useTable } from '../../hooks/use_table'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; export const LogStashPipelinesPage: React.FC = ({ clusters }) => { const globalState = useContext(GlobalStateContext); @@ -43,7 +43,7 @@ export const LogStashPipelinesPage: React.FC = ({ clusters }) => defaultMessage: 'Logstash pipelines', }); - const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { generate: generateBreadcrumbs } = useBreadcrumbContainerContext(); const getPageData = useCallback(async () => { const bounds = services.data?.query.timefilter.timefilter.getBounds(); diff --git a/x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx b/x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx index d0806c1d86c02..ef6707a61d359 100644 --- a/x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx @@ -16,7 +16,7 @@ import { PageTemplate } from '../page_template'; import { CODE_PATH_LICENSE, STANDALONE_CLUSTER_CLUSTER_UUID } from '../../../../common/constants'; import { Legacy } from '../../../legacy_shims'; import { Enabler } from './enabler'; -import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; +import { useBreadcrumbContainerContext } from '../../hooks/use_breadcrumbs'; import { initSetupModeState } from '../../../lib/setup_mode'; import { GlobalStateContext } from '../../contexts/global_state_context'; import { useRequestErrorHandler } from '../../hooks/use_request_error_handler'; @@ -67,7 +67,7 @@ export const NoDataPage = () => { isCollectionIntervalUpdated: false, } as any); - const { update: updateBreadcrumbs } = useContext(BreadcrumbContainer.Context); + const { update: updateBreadcrumbs } = useBreadcrumbContainerContext(); updateBreadcrumbs([ { 'data-test-subj': 'breadcrumbClusters', diff --git a/x-pack/plugins/monitoring/public/application/pages/page_template.tsx b/x-pack/plugins/monitoring/public/application/pages/page_template.tsx index 8e07d196ce588..842c86cc4ab95 100644 --- a/x-pack/plugins/monitoring/public/application/pages/page_template.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/page_template.tsx @@ -20,7 +20,7 @@ import type { IHttpFetchError, ResponseErrorBody } from '@kbn/core-http-browser' import { HeaderMenuPortal } from '@kbn/observability-plugin/public'; import { useTitle } from '../hooks/use_title'; import { MonitoringToolbar } from '../../components/shared/toolbar'; -import { MonitoringTimeContainer } from '../hooks/use_monitoring_time'; +import { useMonitoringTimeContainerContext } from '../hooks/use_monitoring_time'; import { PageLoading } from '../../components'; import { getSetupModeState, @@ -58,7 +58,7 @@ export const PageTemplate: React.FC = ({ }) => { useTitle('', title); - const { currentTimerange } = useContext(MonitoringTimeContainer.Context); + const { currentTimerange } = useMonitoringTimeContainerContext(); const [loaded, setLoaded] = useState(false); const [isRequestPending, setIsRequestPending] = useState(false); const history = useHistory(); diff --git a/x-pack/plugins/monitoring/public/components/shared/toolbar.tsx b/x-pack/plugins/monitoring/public/components/shared/toolbar.tsx index 200ce9f849433..ae90a304a019f 100644 --- a/x-pack/plugins/monitoring/public/components/shared/toolbar.tsx +++ b/x-pack/plugins/monitoring/public/components/shared/toolbar.tsx @@ -9,7 +9,7 @@ import { EuiPageHeader, EuiSuperDatePicker, OnRefreshChangeProps } from '@elasti import React, { useContext, useCallback, useMemo } from 'react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { UI_SETTINGS } from '@kbn/data-plugin/common'; -import { MonitoringTimeContainer } from '../../application/hooks/use_monitoring_time'; +import { useMonitoringTimeContainerContext } from '../../application/hooks/use_monitoring_time'; import { GlobalStateContext } from '../../application/contexts/global_state_context'; import { Legacy } from '../../legacy_shims'; import { MonitoringStartServices } from '../../types'; @@ -49,7 +49,7 @@ export const MonitoringToolbar: React.FC = ({ pageTitle, setIsPaused, isPaused, isDisabled, - } = useContext(MonitoringTimeContainer.Context); + } = useMonitoringTimeContainerContext(); const state = useContext(GlobalStateContext); const onTimeChange = useCallback( diff --git a/x-pack/plugins/observability/public/components/shared/header_menu_portal.tsx b/x-pack/plugins/observability/public/components/shared/header_menu_portal.tsx index 02d16c13ce4e8..dcfe8b9ce85ba 100644 --- a/x-pack/plugins/observability/public/components/shared/header_menu_portal.tsx +++ b/x-pack/plugins/observability/public/components/shared/header_menu_portal.tsx @@ -6,7 +6,7 @@ */ import React, { useEffect, useMemo } from 'react'; -import { createPortalNode, InPortal, OutPortal } from 'react-reverse-portal'; +import { createHtmlPortalNode, InPortal, OutPortal } from 'react-reverse-portal'; import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import { HeaderMenuPortalProps } from './types'; @@ -16,7 +16,7 @@ export default function HeaderMenuPortal({ setHeaderActionMenu, theme$, }: HeaderMenuPortalProps) { - const portalNode = useMemo(() => createPortalNode(), []); + const portalNode = useMemo(() => createHtmlPortalNode(), []); useEffect(() => { setHeaderActionMenu((element) => { diff --git a/x-pack/plugins/osquery/common/schemas/common/utils.ts b/x-pack/plugins/osquery/common/schemas/common/utils.ts index 95b2bdd7a4050..f599346fa0b6b 100644 --- a/x-pack/plugins/osquery/common/schemas/common/utils.ts +++ b/x-pack/plugins/osquery/common/schemas/common/utils.ts @@ -6,14 +6,7 @@ */ import { isEmpty, reduce } from 'lodash'; - -export const convertECSMappingToArray = (ecsMapping: Record | undefined) => - ecsMapping - ? Object.entries(ecsMapping).map((item) => ({ - key: item[0], - value: item[1], - })) - : undefined; +import type { ECSMapping } from './schemas'; export const convertECSMappingToObject = ( ecsMapping: Array<{ @@ -23,7 +16,7 @@ export const convertECSMappingToObject = ( value: string; }; }> -): Record => +): ECSMapping => reduce( ecsMapping, (acc, value) => { diff --git a/x-pack/plugins/osquery/cypress/integration/all/alerts.spec.ts b/x-pack/plugins/osquery/cypress/integration/all/alerts.spec.ts index ee4992b04b1d2..f0b07f89cde92 100644 --- a/x-pack/plugins/osquery/cypress/integration/all/alerts.spec.ts +++ b/x-pack/plugins/osquery/cypress/integration/all/alerts.spec.ts @@ -8,7 +8,6 @@ import { ArchiverMethod, runKbnArchiverScript } from '../../tasks/archiver'; import { login } from '../../tasks/login'; import { - checkResults, findAndClickButton, findFormFieldByRowsLabelAndType, inputQuery, @@ -80,7 +79,8 @@ describe('Alert Event Details', () => { cy.contains('1 agent selected.'); inputQuery('select * from uptime;'); submitQuery(); - checkResults(); + cy.contains('Results'); + cy.contains('Add to timeline investigation'); cy.contains('Save for later').click(); cy.contains('Save query'); cy.get('.euiButtonEmpty--flushLeft').contains('Cancel').click(); diff --git a/x-pack/plugins/osquery/cypress/integration/all/edit_saved_queries.spec.ts b/x-pack/plugins/osquery/cypress/integration/all/edit_saved_queries.spec.ts index 6dde0013a4bc6..42ffd48d73692 100644 --- a/x-pack/plugins/osquery/cypress/integration/all/edit_saved_queries.spec.ts +++ b/x-pack/plugins/osquery/cypress/integration/all/edit_saved_queries.spec.ts @@ -26,15 +26,15 @@ describe('ALL - Edit saved query', () => { }); it('by changing ecs mappings and platforms', () => { + cy.getBySel('pagination-button-next').click(); cy.react('CustomItemAction', { props: { index: 1, item: { attributes: { id: SAVED_QUERY_ID } } }, }).click(); cy.contains('Custom key/value pairs.').should('exist'); cy.contains('Hours of uptime').should('exist'); - cy.react('ECSComboboxFieldComponent', { props: { field: { value: 'labels' } } }) - .parents('[data-test-subj="ECSMappingEditorForm"]') - .react('EuiButtonIcon', { props: { iconType: 'trash' } }) - .click(); + cy.react('ECSMappingEditorForm').within(() => { + cy.react('EuiButtonIcon', { props: { iconType: 'trash' } }).click(); + }); cy.react('PlatformCheckBoxGroupField').within(() => { cy.react('EuiCheckbox', { diff --git a/x-pack/plugins/osquery/cypress/integration/all/live_query.spec.ts b/x-pack/plugins/osquery/cypress/integration/all/live_query.spec.ts index 0678f3170f234..c332e3dae5087 100644 --- a/x-pack/plugins/osquery/cypress/integration/all/live_query.spec.ts +++ b/x-pack/plugins/osquery/cypress/integration/all/live_query.spec.ts @@ -41,6 +41,28 @@ describe('ALL - Live Query', () => { runKbnArchiverScript(ArchiverMethod.UNLOAD, 'example_pack'); }); + it('should validate the form', () => { + cy.contains('New live query').click(); + submitQuery(); + cy.contains('Agents is a required field'); + cy.contains('Query is a required field'); + selectAllAgents(); + inputQuery('select * from uptime; '); + submitQuery(); + cy.contains('Agents is a required field').should('not.exist'); + cy.contains('Query is a required field').should('not.exist'); + checkResults(); + getAdvancedButton().click(); + typeInOsqueryFieldInput('days{downArrow}{enter}'); + submitQuery(); + cy.contains('ECS field is required.'); + typeInECSFieldInput('message{downArrow}{enter}'); + submitQuery(); + cy.contains('ECS field is required.').should('not.exist'); + + checkResults(); + }); + it('should run query and enable ecs mapping', () => { const cmd = Cypress.platform === 'darwin' ? '{meta}{enter}' : '{ctrl}{enter}'; cy.contains('New live query').click(); @@ -82,7 +104,7 @@ describe('ALL - Live Query', () => { cy.contains('New live query').click(); selectAllAgents(); cy.react('SavedQueriesDropdown').type('NOMAPPING{downArrow}{enter}'); - cy.getReact('SavedQueriesDropdown').getCurrentState().should('have.length', 1); + // cy.getReact('SavedQueriesDropdown').getCurrentState().should('have.length', 1); // TODO do we need it? inputQuery('{selectall}{backspace}{selectall}{backspace}select * from users'); cy.wait(1000); submitQuery(); diff --git a/x-pack/plugins/osquery/cypress/integration/all/metrics.spec.ts b/x-pack/plugins/osquery/cypress/integration/all/metrics.spec.ts index 88145dfa46401..0edefdc24ea42 100644 --- a/x-pack/plugins/osquery/cypress/integration/all/metrics.spec.ts +++ b/x-pack/plugins/osquery/cypress/integration/all/metrics.spec.ts @@ -46,7 +46,7 @@ describe('ALL - Inventory', () => { cy.getBySel('comboBoxInput').first().click(); cy.wait(500); - cy.getBySel('comboBoxInput').first().type('{downArrow}{enter}'); + cy.getBySel('comboBoxInput').first().type('saved{downArrow}{enter}'); submitQuery(); checkResults(); diff --git a/x-pack/plugins/osquery/cypress/integration/all/packs.spec.ts b/x-pack/plugins/osquery/cypress/integration/all/packs.spec.ts index 72fae5479a85e..64620cf3cc08c 100644 --- a/x-pack/plugins/osquery/cypress/integration/all/packs.spec.ts +++ b/x-pack/plugins/osquery/cypress/integration/all/packs.spec.ts @@ -77,6 +77,7 @@ describe('ALL - Packs', () => { cy.contains('Attach next query'); inputQuery('select * from uptime'); findFormFieldByRowsLabelAndType('ID', SAVED_QUERY_ID); + cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click(); cy.contains('ID must be unique').should('exist'); findFormFieldByRowsLabelAndType('ID', NEW_QUERY_NAME); cy.contains('ID must be unique').should('not.exist'); @@ -95,6 +96,7 @@ describe('ALL - Packs', () => { cy.contains('Attach next query'); cy.contains('ID must be unique').should('not.exist'); getSavedQueriesDropdown().type(`${SAVED_QUERY_ID}{downArrow}{enter}`); + cy.react('EuiFlyoutFooter').react('EuiButton').contains('Save').click(); cy.contains('ID must be unique').should('exist'); cy.react('EuiFlyoutFooter').react('EuiButtonEmpty').contains('Cancel').click(); }); diff --git a/x-pack/plugins/osquery/cypress/integration/roles/admin.spec.ts b/x-pack/plugins/osquery/cypress/integration/roles/admin.spec.ts index a22177955c4ac..b8781df09d01f 100644 --- a/x-pack/plugins/osquery/cypress/integration/roles/admin.spec.ts +++ b/x-pack/plugins/osquery/cypress/integration/roles/admin.spec.ts @@ -20,7 +20,6 @@ describe('Admin', () => { cy.contains('New live query').click(); selectAllAgents(); inputQuery('select * from uptime; '); - cy.wait(500); submitQuery(); checkResults(); }); diff --git a/x-pack/plugins/osquery/cypress/integration/roles/reader.spec.ts b/x-pack/plugins/osquery/cypress/integration/roles/reader.spec.ts index ddbca76efcf16..365f4d5b0eba1 100644 --- a/x-pack/plugins/osquery/cypress/integration/roles/reader.spec.ts +++ b/x-pack/plugins/osquery/cypress/integration/roles/reader.spec.ts @@ -27,6 +27,7 @@ describe('Reader - only READ', () => { it('should not be able to add nor run saved queries', () => { navigateTo('/app/osquery/saved_queries'); cy.waitForReact(1000); + cy.getBySel('pagination-button-next').click(); cy.contains(SAVED_QUERY_ID); cy.contains('Add saved query').should('be.disabled'); cy.react('PlayButtonComponent', { diff --git a/x-pack/plugins/osquery/cypress/integration/roles/t1_analyst.spec.ts b/x-pack/plugins/osquery/cypress/integration/roles/t1_analyst.spec.ts index c151cce2c2e60..d236555091b3f 100644 --- a/x-pack/plugins/osquery/cypress/integration/roles/t1_analyst.spec.ts +++ b/x-pack/plugins/osquery/cypress/integration/roles/t1_analyst.spec.ts @@ -29,6 +29,7 @@ describe('T1 Analyst - READ + runSavedQueries ', () => { it('should be able to run saved queries but not add new ones', () => { navigateTo('/app/osquery/saved_queries'); cy.waitForReact(1000); + cy.getBySel('pagination-button-next').click(); cy.contains(SAVED_QUERY_ID); cy.contains('Add saved query').should('be.disabled'); cy.react('PlayButtonComponent', { @@ -93,6 +94,7 @@ describe('T1 Analyst - READ + runSavedQueries ', () => { cy.contains('New live query').click(); selectAllAgents(); cy.get(LIVE_QUERY_EDITOR).should('not.exist'); - cy.contains('Submit').should('be.disabled'); + submitQuery(); + cy.contains('Query is a required field'); }); }); diff --git a/x-pack/plugins/osquery/cypress/integration/roles/t2_analyst.spec.ts b/x-pack/plugins/osquery/cypress/integration/roles/t2_analyst.spec.ts index 901b18f1461c7..63a0eb5d1619d 100644 --- a/x-pack/plugins/osquery/cypress/integration/roles/t2_analyst.spec.ts +++ b/x-pack/plugins/osquery/cypress/integration/roles/t2_analyst.spec.ts @@ -100,16 +100,16 @@ describe('T2 Analyst - READ + Write Live/Saved + runSavedQueries ', () => { }); it('to click the edit button and edit pack', () => { navigateTo('/app/osquery/saved_queries'); + cy.getBySel('pagination-button-next').click(); cy.react('CustomItemAction', { props: { index: 1, item: { attributes: { id: SAVED_QUERY_ID } } }, }).click(); cy.contains('Custom key/value pairs.').should('exist'); cy.contains('Hours of uptime').should('exist'); - cy.react('ECSComboboxFieldComponent', { props: { field: { value: 'labels' } } }) - .parents('[data-test-subj="ECSMappingEditorForm"]') - .react('EuiButtonIcon', { props: { iconType: 'trash' } }) - .click(); + cy.react('ECSMappingEditorForm').within(() => { + cy.react('EuiButtonIcon', { props: { iconType: 'trash' } }).click(); + }); cy.react('EuiButton').contains('Update query').click(); cy.wait(5000); diff --git a/x-pack/plugins/osquery/cypress/tasks/live_query.ts b/x-pack/plugins/osquery/cypress/tasks/live_query.ts index 3a1f1b0930edf..3c571d04ff84c 100644 --- a/x-pack/plugins/osquery/cypress/tasks/live_query.ts +++ b/x-pack/plugins/osquery/cypress/tasks/live_query.ts @@ -8,7 +8,7 @@ import { LIVE_QUERY_EDITOR } from '../screens/live_query'; export const DEFAULT_QUERY = 'select * from processes;'; -export const BIG_QUERY = 'select * from processes, users limit 200;'; +export const BIG_QUERY = 'select * from processes, users limit 110;'; export const selectAllAgents = () => { cy.react('AgentsTable').find('input').should('not.be.disabled'); @@ -25,7 +25,10 @@ export const clearInputQuery = () => export const inputQuery = (query: string) => cy.get(LIVE_QUERY_EDITOR).type(query); -export const submitQuery = () => cy.contains('Submit').click(); +export const submitQuery = () => { + cy.wait(1000); // wait for the validation to trigger - cypress is way faster than users ;) + cy.contains('Submit').click(); +}; export const checkResults = () => cy.getBySel('dataGridRowCell', { timeout: 120000 }).should('have.lengthOf.above', 0); diff --git a/x-pack/plugins/osquery/public/action_results/action_results_summary.tsx b/x-pack/plugins/osquery/public/action_results/action_results_summary.tsx index 29d823560d6e3..81feb53fb4012 100644 --- a/x-pack/plugins/osquery/public/action_results/action_results_summary.tsx +++ b/x-pack/plugins/osquery/public/action_results/action_results_summary.tsx @@ -53,9 +53,8 @@ const ActionResultsSummaryComponent: React.FC = ({ skip: !hasActionResultsPrivileges, }); if (expired) { - // @ts-expect-error update types edges.forEach((edge) => { - if (!edge.fields.completed_at) { + if (!edge.fields?.completed_at && edge.fields) { edge.fields['error.keyword'] = edge.fields.error = [ i18n.translate('xpack.osquery.liveQueryActionResults.table.expiredErrorText', { defaultMessage: 'The action request timed out.', diff --git a/x-pack/plugins/osquery/public/action_results/use_action_privileges.tsx b/x-pack/plugins/osquery/public/action_results/use_action_privileges.tsx index 6d0477b22edee..83508def6a483 100644 --- a/x-pack/plugins/osquery/public/action_results/use_action_privileges.tsx +++ b/x-pack/plugins/osquery/public/action_results/use_action_privileges.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { useKibana } from '../common/lib/kibana'; export const useActionResultsPrivileges = () => { diff --git a/x-pack/plugins/osquery/public/action_results/use_action_results.ts b/x-pack/plugins/osquery/public/action_results/use_action_results.ts index 964feb9eafb3b..f950b4f1907c3 100644 --- a/x-pack/plugins/osquery/public/action_results/use_action_results.ts +++ b/x-pack/plugins/osquery/public/action_results/use_action_results.ts @@ -7,7 +7,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { flatten, reverse, uniqBy } from 'lodash/fp'; -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; import { lastValueFrom } from 'rxjs'; import type { InspectResponse } from '../common/helpers'; 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 f0fb0cd35ead6..a4bb5e5037a48 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 @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; import { createFilter } from '../common/helpers'; diff --git a/x-pack/plugins/osquery/public/actions/use_live_query_details.ts b/x-pack/plugins/osquery/public/actions/use_live_query_details.ts index a31c493487057..b8703a8370d02 100644 --- a/x-pack/plugins/osquery/public/actions/use_live_query_details.ts +++ b/x-pack/plugins/osquery/public/actions/use_live_query_details.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; import { useKibana } from '../common/lib/kibana'; diff --git a/x-pack/plugins/osquery/public/agent_policies/use_agent_policies.ts b/x-pack/plugins/osquery/public/agent_policies/use_agent_policies.ts index 5eb70c802140c..37f1789a964de 100644 --- a/x-pack/plugins/osquery/public/agent_policies/use_agent_policies.ts +++ b/x-pack/plugins/osquery/public/agent_policies/use_agent_policies.ts @@ -6,7 +6,7 @@ */ import { mapKeys } from 'lodash'; -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; import type { GetAgentPoliciesResponseItem } from '@kbn/fleet-plugin/common'; diff --git a/x-pack/plugins/osquery/public/agent_policies/use_agent_policy.ts b/x-pack/plugins/osquery/public/agent_policies/use_agent_policy.ts index f629a138f70f6..7a819e1118ad2 100644 --- a/x-pack/plugins/osquery/public/agent_policies/use_agent_policy.ts +++ b/x-pack/plugins/osquery/public/agent_policies/use_agent_policy.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; import type { AgentPolicy } from '@kbn/fleet-plugin/common'; diff --git a/x-pack/plugins/osquery/public/agents/agents_table.tsx b/x-pack/plugins/osquery/public/agents/agents_table.tsx index e11cb9b8277d9..2d05780d6545f 100644 --- a/x-pack/plugins/osquery/public/agents/agents_table.tsx +++ b/x-pack/plugins/osquery/public/agents/agents_table.tsx @@ -35,12 +35,13 @@ import { AGENT_GROUP_KEY } from './types'; interface AgentsTableProps { agentSelection: AgentSelection; onChange: (payload: AgentSelection) => void; + error?: string; } const perPage = 10; const DEBOUNCE_DELAY = 300; // ms -const AgentsTableComponent: React.FC = ({ agentSelection, onChange }) => { +const AgentsTableComponent: React.FC = ({ agentSelection, onChange, error }) => { // search related const [searchValue, setSearchValue] = useState(''); const [modifyingSearch, setModifyingSearch] = useState(false); @@ -185,7 +186,7 @@ const AgentsTableComponent: React.FC = ({ agentSelection, onCh return (
- + { const { http } = useKibana().services; const setErrorToast = useErrorToast(); - const agentResponse = useQueries( - policyIds.map((policyId) => ({ + const agentResponse = useQueries({ + queries: policyIds.map((policyId) => ({ queryKey: ['agentPolicy', policyId], queryFn: () => http.get(`/internal/osquery/fleet_wrapper/agent_policies/${policyId}`), enabled: policyIds.length > 0, onSuccess: () => setErrorToast(), + onError: (error: Error) => setErrorToast(error, { title: i18n.translate('xpack.osquery.action_policy_details.fetchError', { defaultMessage: 'Error while fetching policy details', }), }), - })) - ) as Array>; + })), + }) as Array>; const agentPoliciesLoading = agentResponse.some((p) => p.isLoading); const agentPolicies = agentResponse.map((p) => p.data?.item); diff --git a/x-pack/plugins/osquery/public/agents/use_agent_policy_agent_ids.ts b/x-pack/plugins/osquery/public/agents/use_agent_policy_agent_ids.ts index 4007f14ccc3e0..cd30baf32d52d 100644 --- a/x-pack/plugins/osquery/public/agents/use_agent_policy_agent_ids.ts +++ b/x-pack/plugins/osquery/public/agents/use_agent_policy_agent_ids.ts @@ -7,7 +7,7 @@ import { map } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import type { Agent } from '@kbn/fleet-plugin/common'; import { AGENTS_PREFIX } from '@kbn/fleet-plugin/common'; diff --git a/x-pack/plugins/osquery/public/agents/use_agent_status.ts b/x-pack/plugins/osquery/public/agents/use_agent_status.ts index dd733c27675cd..bc0d4e6323f62 100644 --- a/x-pack/plugins/osquery/public/agents/use_agent_status.ts +++ b/x-pack/plugins/osquery/public/agents/use_agent_status.ts @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import type { GetAgentStatusResponse } from '@kbn/fleet-plugin/common'; import { useErrorToast } from '../common/hooks/use_error_toast'; diff --git a/x-pack/plugins/osquery/public/agents/use_all_agents.ts b/x-pack/plugins/osquery/public/agents/use_all_agents.ts index c5e7c2d703bcf..4f2aa8826f85f 100644 --- a/x-pack/plugins/osquery/public/agents/use_all_agents.ts +++ b/x-pack/plugins/osquery/public/agents/use_all_agents.ts @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import type { ListResult, Agent } from '@kbn/fleet-plugin/common'; import { useErrorToast } from '../common/hooks/use_error_toast'; diff --git a/x-pack/plugins/osquery/public/agents/use_osquery_policies.ts b/x-pack/plugins/osquery/public/agents/use_osquery_policies.ts index 9ecf5d14c6b38..589deab5334b1 100644 --- a/x-pack/plugins/osquery/public/agents/use_osquery_policies.ts +++ b/x-pack/plugins/osquery/public/agents/use_osquery_policies.ts @@ -6,7 +6,7 @@ */ import { uniq } from 'lodash'; -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; import { useKibana } from '../common/lib/kibana'; import { useErrorToast } from '../common/hooks/use_error_toast'; diff --git a/x-pack/plugins/osquery/public/application.tsx b/x-pack/plugins/osquery/public/application.tsx index c57a9cb46795f..b8f8f504a5b5a 100644 --- a/x-pack/plugins/osquery/public/application.tsx +++ b/x-pack/plugins/osquery/public/application.tsx @@ -12,8 +12,8 @@ import ReactDOM from 'react-dom'; import { Router } from 'react-router-dom'; import { I18nProvider } from '@kbn/i18n-react'; import { ThemeProvider } from 'styled-components'; -import { QueryClientProvider } from 'react-query'; -import { ReactQueryDevtools } from 'react-query/devtools'; +import { QueryClientProvider } from '@tanstack/react-query'; +import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import type { Storage } from '@kbn/kibana-utils-plugin/public'; import type { AppMountParameters, CoreStart } from '@kbn/core/public'; diff --git a/x-pack/plugins/osquery/public/assets/use_assets_status.ts b/x-pack/plugins/osquery/public/assets/use_assets_status.ts index 28e752df6b2f4..0d082f29b0175 100644 --- a/x-pack/plugins/osquery/public/assets/use_assets_status.ts +++ b/x-pack/plugins/osquery/public/assets/use_assets_status.ts @@ -6,7 +6,7 @@ */ import type { SavedObject } from '@kbn/core/public'; -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { useKibana } from '../common/lib/kibana'; import { INTEGRATION_ASSETS_STATUS_ID } from './constants'; diff --git a/x-pack/plugins/osquery/public/assets/use_import_assets.ts b/x-pack/plugins/osquery/public/assets/use_import_assets.ts index feb2c48041567..4ae6d8549d6fa 100644 --- a/x-pack/plugins/osquery/public/assets/use_import_assets.ts +++ b/x-pack/plugins/osquery/public/assets/use_import_assets.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useMutation, useQueryClient } from 'react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useKibana } from '../common/lib/kibana'; import { useErrorToast } from '../common/hooks/use_error_toast'; import { PACKS_ID } from '../packs/constants'; @@ -26,8 +26,8 @@ export const useImportAssets = ({ successToastText }: UseImportAssetsProps) => { return useMutation(() => http.post('/internal/osquery/assets/update'), { onSuccess: () => { setErrorToast(); - queryClient.invalidateQueries(PACKS_ID); - queryClient.invalidateQueries(INTEGRATION_ASSETS_STATUS_ID); + queryClient.invalidateQueries([PACKS_ID]); + queryClient.invalidateQueries([INTEGRATION_ASSETS_STATUS_ID]); toasts.addSuccess(successToastText); }, onError: (error) => { diff --git a/x-pack/plugins/osquery/public/common/helpers.ts b/x-pack/plugins/osquery/public/common/helpers.ts index 6b0a5f2a51c39..6ac79d9ed8870 100644 --- a/x-pack/plugins/osquery/public/common/helpers.ts +++ b/x-pack/plugins/osquery/public/common/helpers.ts @@ -15,7 +15,6 @@ import type { } from '../../common/search_strategy'; import type { ESQuery } from '../../common/typed_json'; -import type { ArrayItem } from '../shared_imports'; export const createFilter = (filterQuery: ESQuery | string | undefined) => isString(filterQuery) ? filterQuery : JSON.stringify(filterQuery); @@ -44,7 +43,7 @@ export const getInspectResponse = ( response != null ? [JSON.stringify(response.rawResponse, null, 2)] : prevResponse?.response, }); -export const prepareEcsFieldsToValidate = (ecsMapping: ArrayItem[]): string[] => +export const prepareEcsFieldsToValidate = (ecsMapping: Array<{ id: string }>): string[] => ecsMapping ?.map((_: unknown, index: number) => [ `ecs_mapping[${index}].result.value`, diff --git a/x-pack/plugins/osquery/public/common/hooks/use_logs_data_view.tsx b/x-pack/plugins/osquery/public/common/hooks/use_logs_data_view.tsx index 1ac76a54b6729..cf5a9e42d8911 100644 --- a/x-pack/plugins/osquery/public/common/hooks/use_logs_data_view.tsx +++ b/x-pack/plugins/osquery/public/common/hooks/use_logs_data_view.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import type { DataView } from '@kbn/data-plugin/common'; import { useKibana } from '../lib/kibana'; diff --git a/x-pack/plugins/osquery/public/common/hooks/use_osquery_integration.tsx b/x-pack/plugins/osquery/public/common/hooks/use_osquery_integration.tsx index 92f32d6535b4c..4602b8df24d78 100644 --- a/x-pack/plugins/osquery/public/common/hooks/use_osquery_integration.tsx +++ b/x-pack/plugins/osquery/public/common/hooks/use_osquery_integration.tsx @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { useKibana } from '../lib/kibana'; import { useErrorToast } from './use_error_toast'; @@ -16,7 +16,7 @@ export const useOsqueryIntegrationStatus = () => { const setErrorToast = useErrorToast(); return useQuery( - 'integration', + ['integration'], () => http.get<{ name: string; version: string; title: string; install_status: string }>( '/internal/osquery/status' diff --git a/x-pack/plugins/osquery/public/common/validations.ts b/x-pack/plugins/osquery/public/common/validations.ts deleted file mode 100644 index 4a29d274fcc0c..0000000000000 --- a/x-pack/plugins/osquery/public/common/validations.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; - -import type { FormData, ValidationFunc } from '../shared_imports'; -import { fieldValidators } from '../shared_imports'; - -export const queryFieldValidation: ValidationFunc = - fieldValidators.emptyField( - i18n.translate('xpack.osquery.pack.queryFlyoutForm.emptyQueryError', { - defaultMessage: 'Query is a required field', - }) - ); diff --git a/x-pack/plugins/osquery/public/editor/index.tsx b/x-pack/plugins/osquery/public/editor/index.tsx index 2c6a505af55cd..583e3558354e8 100644 --- a/x-pack/plugins/osquery/public/editor/index.tsx +++ b/x-pack/plugins/osquery/public/editor/index.tsx @@ -7,12 +7,12 @@ import React, { useEffect, useState } from 'react'; import useDebounce from 'react-use/lib/useDebounce'; -import 'brace/theme/tomorrow'; import type { EuiCodeEditorProps } from '../shared_imports'; import { EuiCodeEditor } from '../shared_imports'; import './osquery_mode'; +import 'brace/theme/tomorrow'; const EDITOR_SET_OPTIONS = { enableBasicAutocompletion: true, diff --git a/x-pack/plugins/osquery/public/form/index.ts b/x-pack/plugins/osquery/public/form/index.ts new file mode 100644 index 0000000000000..31ea2ac171c88 --- /dev/null +++ b/x-pack/plugins/osquery/public/form/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { VersionField } from './version_field'; +export { QueryDescriptionField } from './query_description_field'; +export { IntervalField } from './interval_field'; +export { QueryIdField } from './query_id_field'; +export type { FormField } from './types'; diff --git a/x-pack/plugins/osquery/public/form/interval_field.tsx b/x-pack/plugins/osquery/public/form/interval_field.tsx new file mode 100644 index 0000000000000..d8d1a01804d65 --- /dev/null +++ b/x-pack/plugins/osquery/public/form/interval_field.tsx @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { useCallback, useMemo } from 'react'; + +import { useController } from 'react-hook-form'; +import type { EuiFieldNumberProps } from '@elastic/eui'; +import { EuiFieldNumber, EuiFormRow } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +const intervalFieldValidations = { + required: { + message: i18n.translate('xpack.osquery.pack.queryFlyoutForm.intervalFieldMinNumberError', { + defaultMessage: 'A positive interval value is required', + }), + value: true, + }, + min: { + message: i18n.translate('xpack.osquery.pack.queryFlyoutForm.intervalFieldMinNumberError', { + defaultMessage: 'A positive interval value is required', + }), + value: 1, + }, + max: { + message: i18n.translate('xpack.osquery.pack.queryFlyoutForm.intervalFieldMaxNumberError', { + defaultMessage: 'An interval value must be lower than {than}', + values: { than: 604800 }, + }), + value: 604800, + }, +}; + +interface IntervalFieldProps { + euiFieldProps?: Record; +} + +const IntervalFieldComponent = ({ euiFieldProps }: IntervalFieldProps) => { + const { + field: { onChange, value }, + fieldState: { error }, + } = useController({ + name: 'interval', + defaultValue: 3600, + rules: { + ...intervalFieldValidations, + }, + }); + const handleChange = useCallback( + (e: React.ChangeEvent) => { + const numberValue = e.target.valueAsNumber ? e.target.valueAsNumber : 0; + onChange(numberValue); + }, + [onChange] + ); + const hasError = useMemo(() => !!error?.message, [error?.message]); + + return ( + + + + ); +}; + +export const IntervalField = React.memo(IntervalFieldComponent); diff --git a/x-pack/plugins/osquery/public/form/query_description_field.tsx b/x-pack/plugins/osquery/public/form/query_description_field.tsx new file mode 100644 index 0000000000000..ae4a216b603f5 --- /dev/null +++ b/x-pack/plugins/osquery/public/form/query_description_field.tsx @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { useMemo } from 'react'; +import { useController } from 'react-hook-form'; +import { EuiFieldText, EuiFormRow } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +interface QueryDescriptionFieldProps { + euiFieldProps?: Record; +} + +const QueryDescriptionFieldComponentn = ({ euiFieldProps }: QueryDescriptionFieldProps) => { + const { + field: { onChange, value, name: fieldName }, + fieldState: { error }, + } = useController({ + name: 'description', + defaultValue: '', + }); + + const hasError = useMemo(() => !!error?.message, [error?.message]); + + return ( + + + + ); +}; + +export const QueryDescriptionField = React.memo(QueryDescriptionFieldComponentn); diff --git a/x-pack/plugins/osquery/public/form/query_id_field.tsx b/x-pack/plugins/osquery/public/form/query_id_field.tsx new file mode 100644 index 0000000000000..1c0abfde5113f --- /dev/null +++ b/x-pack/plugins/osquery/public/form/query_id_field.tsx @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { useMemo } from 'react'; +import { useController } from 'react-hook-form'; +import { EuiFieldText, EuiFormRow } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { createFormIdFieldValidations } from '../packs/queries/validations'; + +interface QueryIdFieldProps { + idSet?: Set; + euiFieldProps?: Record; +} + +const QueryIdFieldComponentn = ({ idSet, euiFieldProps }: QueryIdFieldProps) => { + const { + field: { onChange, value, name: fieldName }, + fieldState: { error }, + } = useController({ + name: 'id', + defaultValue: '', + rules: idSet && createFormIdFieldValidations(idSet), + }); + + const hasError = useMemo(() => !!error?.message, [error?.message]); + + return ( + + + + ); +}; + +export const QueryIdField = React.memo(QueryIdFieldComponentn); diff --git a/x-pack/plugins/osquery/public/form/types.ts b/x-pack/plugins/osquery/public/form/types.ts new file mode 100644 index 0000000000000..14f013ac2b012 --- /dev/null +++ b/x-pack/plugins/osquery/public/form/types.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type React from 'react'; +import type { ReactNode } from 'react'; + +export interface FormField { + name: string; + onChange: (data: T) => void; + value: T; + onBlur?: () => void; +} + +export interface FormFieldProps { + name: string; + label: string | Element; + labelAppend?: ReactNode; + helpText?: string | (() => React.ReactNode); + idAria?: string; + euiFieldProps?: Record; + defaultValue?: T; + required?: boolean; + rules?: Record; +} diff --git a/x-pack/plugins/osquery/public/form/version_field.tsx b/x-pack/plugins/osquery/public/form/version_field.tsx new file mode 100644 index 0000000000000..f97e26b79a651 --- /dev/null +++ b/x-pack/plugins/osquery/public/form/version_field.tsx @@ -0,0 +1,88 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import type { EuiComboBoxOptionOption } from '@elastic/eui'; +import { EuiFormRow, EuiComboBox, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { useController } from 'react-hook-form'; +import { FormattedMessage } from '@kbn/i18n-react'; + +interface VersionFieldProps { + euiFieldProps?: Record; +} +const VersionFieldComponent = ({ euiFieldProps = {} }: VersionFieldProps) => { + const { + field: { onChange, value }, + fieldState: { error }, + } = useController({ + name: 'version', + defaultValue: [], + rules: {}, + }); + + const onCreateComboOption = useCallback( + (newValue: string) => { + const result = [...(value as string[]), newValue]; + + onChange(result); + }, + [onChange, value] + ); + + const onComboChange = useCallback( + (options: EuiComboBoxOptionOption[]) => { + onChange(options.map((option) => option.label)); + }, + [onChange] + ); + const hasError = useMemo(() => !!error?.message, [error?.message]); + + return ( + + + + + + } + labelAppend={ + + + + + + } + error={error?.message} + isInvalid={hasError} + fullWidth + > + ({ label: v }))} + onCreateOption={onCreateComboOption} + onChange={onComboChange} + fullWidth + data-test-subj="input" + {...euiFieldProps} + /> + + ); +}; + +export const VersionField = React.memo(VersionFieldComponent); diff --git a/x-pack/plugins/osquery/public/live_queries/form/agents_table_field.tsx b/x-pack/plugins/osquery/public/live_queries/form/agents_table_field.tsx index b4fd3bdaf216c..eab43c5982b80 100644 --- a/x-pack/plugins/osquery/public/live_queries/form/agents_table_field.tsx +++ b/x-pack/plugins/osquery/public/live_queries/form/agents_table_field.tsx @@ -5,27 +5,47 @@ * 2.0. */ -import React, { useCallback } from 'react'; -import type { FieldHook } from '../../shared_imports'; +import React from 'react'; +import { useController } from 'react-hook-form'; +import { isEmpty } from 'lodash'; +import { i18n } from '@kbn/i18n'; import { AgentsTable } from '../../agents/agents_table'; import type { AgentSelection } from '../../agents/types'; -interface AgentsTableFieldProps { - field: FieldHook; -} +const checkAgentsLength = (agentsSelection: AgentSelection) => { + if (!isEmpty(agentsSelection)) { + const isValid = !!( + agentsSelection.allAgentsSelected || + agentsSelection.agents?.length || + agentsSelection.platformsSelected?.length || + agentsSelection.policiesSelected?.length + ); -const AgentsTableFieldComponent: React.FC = ({ field }) => { - const { value, setValue } = field; - const handleChange = useCallback( - (props) => { - if (props !== value) { - return setValue(props); - } + return !isValid + ? i18n.translate('xpack.osquery.pack.queryFlyoutForm.osqueryAgentsMissingErrorMessage', { + defaultMessage: 'Agents is a required field', + }) + : undefined; + } + + return i18n.translate('xpack.osquery.pack.queryFlyoutForm.osqueryAgentsMissingErrorMessage', { + defaultMessage: 'Agents is a required field', + }); +}; + +const AgentsTableFieldComponent: React.FC<{}> = () => { + const { + field: { onChange, value }, + fieldState: { error }, + } = useController({ + name: 'agentSelection', + rules: { + validate: checkAgentsLength, }, - [value, setValue] - ); + defaultValue: {}, + }); - return ; + return ; }; export const AgentsTableField = React.memo(AgentsTableFieldComponent); diff --git a/x-pack/plugins/osquery/public/live_queries/form/index.tsx b/x-pack/plugins/osquery/public/live_queries/form/index.tsx index ed4059884a9af..3ed54c451f38b 100644 --- a/x-pack/plugins/osquery/public/live_queries/form/index.tsx +++ b/x-pack/plugins/osquery/public/live_queries/form/index.tsx @@ -19,27 +19,47 @@ import { import { FormattedMessage } from '@kbn/i18n-react'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; +import { useForm as useHookForm, FormProvider } from 'react-hook-form'; -import { pickBy, isEmpty, map, find } from 'lodash'; +import { isEmpty, map, find, pickBy } from 'lodash'; import { i18n } from '@kbn/i18n'; +import type { SavedQuerySOFormData } from '../../saved_queries/form/use_saved_query_form'; +import type { + EcsMappingFormField, + EcsMappingSerialized, +} from '../../packs/queries/ecs_mapping_editor_field'; +import { defaultEcsFormData } from '../../packs/queries/ecs_mapping_editor_field'; import { convertECSMappingToObject } from '../../../common/schemas/common/utils'; -import type { FormData } from '../../shared_imports'; -import { UseField, Form, useForm, useFormData } from '../../shared_imports'; -import { AgentsTableField } from './agents_table_field'; -import { LiveQueryQueryField } from './live_query_query_field'; import { useKibana } from '../../common/lib/kibana'; import { ResultTabs } from '../../routes/saved_queries/edit/tabs'; import { SavedQueryFlyout } from '../../saved_queries'; import { ECSMappingEditorField } from '../../packs/queries/lazy_ecs_mapping_editor_field'; import { SavedQueriesDropdown } from '../../saved_queries/saved_queries_dropdown'; -import { liveQueryFormSchema } from './schema'; import { usePacks } from '../../packs/use_packs'; import { PackQueriesStatusTable } from './pack_queries_status_table'; import { useCreateLiveQuery } from '../use_create_live_query_action'; import { useLiveQueryDetails } from '../../actions/use_live_query_details'; +import type { AgentSelection } from '../../agents/types'; +import { LiveQueryQueryField } from './live_query_query_field'; +import { AgentsTableField } from './agents_table_field'; import { PacksComboBoxField } from './packs_combobox_field'; +import { savedQueryDataSerializer } from '../../saved_queries/form/use_saved_query_form'; + +export interface LiveQueryFormFields { + query?: string; + agentSelection: AgentSelection; + savedQueryId?: string | null; + ecs_mapping: EcsMappingFormField[]; + packId: string[]; +} -const FORM_ID = 'liveQueryForm'; +interface DefaultLiveQueryFormFields { + query?: string; + agentSelection?: AgentSelection; + savedQueryId?: string | null; + ecs_mapping?: EcsMappingSerialized; + packId?: string; +} const StyledEuiCard = styled(EuiCard)` padding: 16px 92px 16px 16px !important; @@ -86,12 +106,10 @@ const StyledEuiAccordion = styled(EuiAccordion)` } `; -const GhostFormField = () => <>; - type FormType = 'simple' | 'steps'; interface LiveQueryFormProps { - defaultValue?: Partial; + defaultValue?: DefaultLiveQueryFormFields; onSuccess?: () => void; queryField?: boolean; ecsMappingField?: boolean; @@ -118,6 +136,22 @@ const LiveQueryFormComponent: React.FC = ({ [permissions] ); + const hooksForm = useHookForm({ + defaultValues: { + ecs_mapping: [defaultEcsFormData], + }, + }); + const { + handleSubmit, + watch, + setValue, + resetField, + clearErrors, + getFieldState, + register, + formState: { isSubmitting, errors }, + } = hooksForm; + const canRunSingleQuery = useMemo( () => !!( @@ -133,6 +167,8 @@ const LiveQueryFormComponent: React.FC = ({ const [queryType, setQueryType] = useState('query'); const [isLive, setIsLive] = useState(false); + const queryState = getFieldState('query'); + const watchedValues = watch(); const handleShowSaveQueryFlyout = useCallback(() => setShowSavedQueryFlyout(true), []); const handleCloseSaveQueryFlyout = useCallback(() => setShowSavedQueryFlyout(false), []); @@ -150,75 +186,22 @@ const LiveQueryFormComponent: React.FC = ({ isLive, }); - const { form } = useForm({ - id: FORM_ID, - schema: liveQueryFormSchema, - onSubmit: async (formData, isValid) => { - if (isValid) { - try { - // @ts-expect-error update types - await mutateAsync(formData); - // eslint-disable-next-line no-empty - } catch (e) {} - } - }, - options: { - stripEmptyFields: false, - }, - serializer: ({ - savedQueryId, - // eslint-disable-next-line @typescript-eslint/naming-convention - ecs_mapping, - packId, - ...formData - }) => - pickBy( - { - ...formData, - pack_id: packId?.length ? packId[0] : undefined, - saved_query_id: savedQueryId, - ecs_mapping: convertECSMappingToObject(ecs_mapping), - }, - (value) => !isEmpty(value) - ), - }); - - const { updateFieldValues, setFieldValue, submit, isSubmitting } = form; - const actionId = useMemo(() => liveQueryDetails?.action_id, [liveQueryDetails?.action_id]); const agentIds = useMemo(() => liveQueryDetails?.agents, [liveQueryDetails?.agents]); - const [ - { agentSelection, ecs_mapping: ecsMapping, query, savedQueryId, packId }, - formDataSerializer, - ] = useFormData({ - form, - }); - /* recalculate the form data when ecs_mapping changes */ - // eslint-disable-next-line react-hooks/exhaustive-deps - const serializedFormData = useMemo(() => formDataSerializer(), [ecsMapping, formDataSerializer]); - - const agentSelected = useMemo( - () => - agentSelection && - !!( - agentSelection.allAgentsSelected || - agentSelection.agents?.length || - agentSelection.platformsSelected?.length || - agentSelection.policiesSelected?.length - ), - [agentSelection] - ); + useEffect(() => { + register('savedQueryId'); + }, [register]); - const queryValueProvided = useMemo(() => !!query?.length, [query]); + const { packId } = watchedValues; const queryStatus = useMemo(() => { - if (isError || !form.getFields().query?.isValid) return 'danger'; + if (isError || queryState.invalid) return 'danger'; if (isLoading) return 'loading'; if (isSuccess) return 'complete'; return 'incomplete'; - }, [isError, isLoading, isSuccess, form]); + }, [isError, isLoading, isSuccess, queryState]); const resultsStatus = useMemo( () => (queryStatus === 'complete' ? 'incomplete' : 'disabled'), @@ -228,39 +211,66 @@ const LiveQueryFormComponent: React.FC = ({ const handleSavedQueryChange = useCallback( (savedQuery) => { if (savedQuery) { - updateFieldValues({ - query: savedQuery.query, - savedQueryId: savedQuery.savedQueryId, - ecs_mapping: savedQuery.ecs_mapping + setValue('query', savedQuery.query); + setValue('savedQueryId', savedQuery.savedQueryId); + setValue( + 'ecs_mapping', + !isEmpty(savedQuery.ecs_mapping) ? map(savedQuery.ecs_mapping, (value, key) => ({ key, result: { type: Object.keys(value)[0], - value: Object.values(value)[0], + value: Object.values(value)[0] as string, }, })) - : [], - }); + : [defaultEcsFormData] + ); if (!isEmpty(savedQuery.ecs_mapping)) { setAdvancedContentState('open'); } } else { - setFieldValue('savedQueryId', null); + setValue('savedQueryId', null); } }, - [setFieldValue, updateFieldValues] + [setValue] ); + const onSubmit = useCallback( + // not sure why, but submitOnCmdEnter doesn't have proper form values so I am passing them in manually + async (values: LiveQueryFormFields = watchedValues) => { + const serializedData = pickBy( + { + agentSelection: values.agentSelection, + saved_query_id: values.savedQueryId, + query: values.query, + pack_id: packId?.length ? packId[0] : undefined, + ...(values.ecs_mapping + ? { ecs_mapping: convertECSMappingToObject(values.ecs_mapping) } + : {}), + }, + (value) => !isEmpty(value) + ); + if (isEmpty(errors)) { + try { + // @ts-expect-error update types + await mutateAsync(serializedData); + // eslint-disable-next-line no-empty + } catch (e) {} + } + }, + [errors, mutateAsync, packId, watchedValues] + ); const commands = useMemo( () => [ { name: 'submitOnCmdEnter', bindKey: { win: 'ctrl+enter', mac: 'cmd+enter' }, - exec: () => submit(), + // @ts-expect-error update types - explanation in onSubmit() + exec: () => handleSubmit(onSubmit)(watchedValues), }, ], - [submit] + [handleSubmit, onSubmit, watchedValues] ); const queryComponentProps = useMemo( @@ -270,9 +280,9 @@ const LiveQueryFormComponent: React.FC = ({ [commands] ); - const flyoutFormDefaultValue = useMemo( - () => ({ savedQueryId, query, ecs_mapping: serializedFormData.ecs_mapping }), - [savedQueryId, serializedFormData.ecs_mapping, query] + const serializedData: SavedQuerySOFormData = useMemo( + () => savedQueryDataSerializer(watchedValues), + [watchedValues] ); const handleToggle = useCallback((isOpen) => { @@ -306,12 +316,7 @@ const LiveQueryFormComponent: React.FC = ({ {formType === 'steps' && queryType !== 'pack' && ( = ({ = ({ ), [ - agentSelected, - enabled, formType, - handleShowSaveQueryFlyout, - isSubmitting, - packId, - permissions.writeSavedQueries, queryType, - queryValueProvided, + permissions.writeSavedQueries, resultsStatus, - selectedPackData, - submit, + handleShowSaveQueryFlyout, + enabled, + isSubmitting, + handleSubmit, + onSubmit, ] ); const queryFieldStepContent = useMemo( () => ( <> - {queryField ? ( + {queryField && ( <> {!isSavedQueryDisabled && ( <> @@ -372,20 +367,10 @@ const LiveQueryFormComponent: React.FC = ({ /> )} - - - - ) : ( - <> - - + )} - {ecsMappingField ? ( + {ecsMappingField && ( <> = ({ - ) : ( - )} ), [ queryField, - queryComponentProps, + isSavedQueryDisabled, handleSavedQueryChange, + queryComponentProps, + queryType, ecsMappingField, advancedContentState, handleToggle, ecsFieldProps, - isSavedQueryDisabled, ] ); @@ -422,7 +406,7 @@ const LiveQueryFormComponent: React.FC = ({ singleQueryDetails?.action_id ? ( = ({ singleQueryDetails?.action_id, singleQueryDetails?.expiration, singleQueryDetails?.agents, - serializedFormData.ecs_mapping, + serializedData.ecs_mapping, addToTimeline, ] ); @@ -440,9 +424,7 @@ const LiveQueryFormComponent: React.FC = ({ useEffect(() => { if (defaultValue) { if (defaultValue.agentSelection) { - updateFieldValues({ - agentSelection: defaultValue.agentSelection, - }); + setValue('agentSelection', defaultValue.agentSelection); } if (defaultValue?.packId && canRunPacks) { @@ -451,19 +433,18 @@ const LiveQueryFormComponent: React.FC = ({ if (!isPackDataFetched) return; const selectedPackOption = find(packsData?.data, ['id', defaultValue.packId]); if (selectedPackOption) { - updateFieldValues({ - packId: [defaultValue.packId], - }); + setValue('packId', [defaultValue.packId]); } return; } if (defaultValue?.query && canRunSingleQuery) { - updateFieldValues({ - query: defaultValue.query, - savedQueryId: defaultValue.savedQueryId, - ecs_mapping: defaultValue.ecs_mapping + setValue('query', defaultValue.query); + setValue('savedQueryId', defaultValue.savedQueryId); + setValue( + 'ecs_mapping', + !isEmpty(defaultValue.ecs_mapping) ? map(defaultValue.ecs_mapping, (value, key) => ({ key, result: { @@ -471,8 +452,8 @@ const LiveQueryFormComponent: React.FC = ({ value: Object.values(value)[0], }, })) - : undefined, - }); + : [defaultEcsFormData] + ); return; } @@ -485,14 +466,7 @@ const LiveQueryFormComponent: React.FC = ({ return setQueryType('pack'); } } - }, [ - canRunPacks, - canRunSingleQuery, - defaultValue, - isPackDataFetched, - packsData?.data, - updateFieldValues, - ]); + }, [canRunPacks, canRunSingleQuery, defaultValue, isPackDataFetched, packsData?.data, setValue]); const queryCardSelectable = useMemo( () => ({ @@ -516,11 +490,20 @@ const LiveQueryFormComponent: React.FC = ({ setIsLive(() => !(liveQueryDetails?.status === 'completed')); }, [liveQueryDetails?.status]); - useEffect(() => cleanupLiveQuery(), [queryType, packId, cleanupLiveQuery]); + useEffect(() => { + cleanupLiveQuery(); + if (!defaultValue) { + resetField('packId'); + resetField('query'); + resetField('ecs_mapping'); + resetField('savedQueryId'); + clearErrors(); + } + }, [queryType, cleanupLiveQuery, resetField, setValue, clearErrors, defaultValue]); return ( <> -
+ {queryField && ( @@ -572,25 +555,23 @@ const LiveQueryFormComponent: React.FC = ({ )} - {!hideAgentsField ? ( + {!hideAgentsField && ( - + - ) : ( - )} {queryType === 'pack' ? ( <> - {submitButtonContent} + {liveQueryDetails?.queries?.length || selectedPackData?.attributes?.queries?.length ? ( <> @@ -598,6 +579,7 @@ const LiveQueryFormComponent: React.FC = ({ @@ -613,12 +595,13 @@ const LiveQueryFormComponent: React.FC = ({ )} - +
+ {showSavedQueryFlyout ? ( ) : null} diff --git a/x-pack/plugins/osquery/public/live_queries/form/live_query_query_field.tsx b/x-pack/plugins/osquery/public/live_queries/form/live_query_query_field.tsx index 86775040c7f73..e3516f982cc0b 100644 --- a/x-pack/plugins/osquery/public/live_queries/form/live_query_query_field.tsx +++ b/x-pack/plugins/osquery/public/live_queries/form/live_query_query_field.tsx @@ -6,12 +6,15 @@ */ import { EuiCodeBlock, EuiFormRow } from '@elastic/eui'; -import React, { useCallback } from 'react'; +import React from 'react'; import styled from 'styled-components'; -import type { EuiCodeEditorProps, FieldHook } from '../../shared_imports'; +import { useController } from 'react-hook-form'; +import { i18n } from '@kbn/i18n'; +import type { EuiCodeEditorProps } from '../../shared_imports'; import { OsqueryEditor } from '../../editor'; import { useKibana } from '../../common/lib/kibana'; +import { MAX_QUERY_LENGTH } from '../../packs/queries/validations'; const StyledEuiCodeBlock = styled(EuiCodeBlock)` min-height: 100px; @@ -19,30 +22,44 @@ const StyledEuiCodeBlock = styled(EuiCodeBlock)` interface LiveQueryQueryFieldProps { disabled?: boolean; - field: FieldHook; commands?: EuiCodeEditorProps['commands']; + queryType: string; } const LiveQueryQueryFieldComponent: React.FC = ({ disabled, - field, commands, + queryType, }) => { const permissions = useKibana().services.application.capabilities.osquery; - const { value, setValue, errors } = field; - const error = errors[0]?.message; - const handleEditorChange = useCallback( - (newValue) => { - setValue(newValue); + const { + field: { onChange, value }, + fieldState: { error }, + } = useController({ + name: 'query', + rules: { + required: { + message: i18n.translate('xpack.osquery.pack.queryFlyoutForm.emptyQueryError', { + defaultMessage: 'Query is a required field', + }), + value: queryType === 'query', + }, + maxLength: { + message: i18n.translate('xpack.osquery.liveQuery.queryForm.largeQueryError', { + defaultMessage: 'Query is too large (max {maxLength} characters)', + values: { maxLength: MAX_QUERY_LENGTH }, + }), + value: MAX_QUERY_LENGTH, + }, }, - [setValue] - ); + defaultValue: '', + }); return ( @@ -56,7 +73,7 @@ const LiveQueryQueryFieldComponent: React.FC = ({ {value} ) : ( - + )} ); diff --git a/x-pack/plugins/osquery/public/live_queries/form/packs_combobox_field.tsx b/x-pack/plugins/osquery/public/live_queries/form/packs_combobox_field.tsx index e9cf7fa6f9e85..0d6c93d23ed30 100644 --- a/x-pack/plugins/osquery/public/live_queries/form/packs_combobox_field.tsx +++ b/x-pack/plugins/osquery/public/live_queries/form/packs_combobox_field.tsx @@ -12,8 +12,7 @@ import type { EuiComboBoxOptionOption } from '@elastic/eui'; import { EuiFormRow, EuiComboBox, EuiTextColor, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import styled from 'styled-components'; -import type { FieldHook } from '../../shared_imports'; -import { VALIDATION_TYPES } from '../../shared_imports'; +import { useController } from 'react-hook-form'; import type { PackSavedObject } from '../../packs/types'; const TextTruncate = styled.div` @@ -21,13 +20,12 @@ const TextTruncate = styled.div` text-overflow: ellipsis; `; -interface Props { - field: FieldHook; - euiFieldProps?: { +interface PackComboBoxFieldProps { + fieldProps?: { packsData?: PackSavedObject[]; }; idAria?: string; - [key: string]: unknown; + queryType: string; } interface PackOption { @@ -36,48 +34,53 @@ interface PackOption { description?: string; } -export const PacksComboBoxField = ({ field, euiFieldProps = {}, idAria, ...rest }: Props) => { +export const PacksComboBoxField = ({ + queryType, + fieldProps = {}, + idAria, + ...rest +}: PackComboBoxFieldProps) => { + const { + field: { value, onChange }, + fieldState, + } = useController({ + name: 'packId', + rules: { + required: { + message: i18n.translate( + 'xpack.osquery.pack.queryFlyoutForm.osqueryPackMissingErrorMessage', + { + defaultMessage: 'Pack is a required field', + } + ), + value: queryType === 'pack', + }, + }, + defaultValue: [], + }); + const error = fieldState.error?.message; const [selectedOptions, setSelectedOptions] = useState< Array> >([]); - // Errors for the comboBox value (the "array") - const errorMessageField = field.getErrorsMessages(); - - // Errors for comboBox option added (the array "item") - const errorMessageArrayItem = field.getErrorsMessages({ - validationType: VALIDATION_TYPES.ARRAY_ITEM, - }); - - const isInvalid = field.errors.length - ? errorMessageField !== null || errorMessageArrayItem !== null - : false; - - // Concatenate error messages. - const errorMessage = - errorMessageField && errorMessageArrayItem - ? `${errorMessageField}, ${errorMessageArrayItem}` - : errorMessageField - ? errorMessageField - : errorMessageArrayItem; const handlePackChange = useCallback( (newSelectedOptions) => { if (!newSelectedOptions.length) { setSelectedOptions(newSelectedOptions); - field.setValue([]); + onChange([]); return; } setSelectedOptions(newSelectedOptions); - field.setValue([newSelectedOptions[0].value?.id]); + onChange([newSelectedOptions[0].value?.id]); }, - [field] + [onChange] ); const packOptions = useMemo>>( () => - euiFieldProps?.packsData?.map((packSO) => ({ + fieldProps?.packsData?.map((packSO) => ({ label: packSO.attributes.name ?? '', value: { id: packSO.id, @@ -85,20 +88,11 @@ export const PacksComboBoxField = ({ field, euiFieldProps = {}, idAria, ...rest description: packSO.attributes.description, }, })) ?? [], - [euiFieldProps?.packsData] - ); - - const onSearchComboChange = useCallback( - (value: string) => { - if (value !== undefined) { - field.clearErrors(VALIDATION_TYPES.ARRAY_ITEM); - } - }, - [field] + [fieldProps?.packsData] ); const renderOption = useCallback( - ({ value }) => ( + ({ value: option }) => ( - {value.name} + {option?.name} - {value.description} + {option?.description} @@ -119,22 +113,22 @@ export const PacksComboBoxField = ({ field, euiFieldProps = {}, idAria, ...rest ); useEffect(() => { - if (field.value.length) { - const packOption = find(packOptions, ['value.id', field.value[0]]); + if (value?.length) { + const packOption = find(packOptions, ['value.id', value[0]]); if (packOption) { setSelectedOptions([packOption]); } } - }, [field.value, packOptions]); + }, [value, packOptions]); return ( ); diff --git a/x-pack/plugins/osquery/public/live_queries/form/schema.ts b/x-pack/plugins/osquery/public/live_queries/form/schema.ts deleted file mode 100644 index c09eccd1c5c88..0000000000000 --- a/x-pack/plugins/osquery/public/live_queries/form/schema.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export const MAX_QUERY_LENGTH = 2000; - -import { i18n } from '@kbn/i18n'; -import { FIELD_TYPES } from '../../shared_imports'; -import { queryFieldValidation } from '../../common/validations'; -import { fieldValidators } from '../../shared_imports'; - -export const liveQueryFormSchema = { - agentSelection: { - defaultValue: { - agents: [], - allAgentsSelected: false, - platformsSelected: [], - policiesSelected: [], - }, - type: FIELD_TYPES.JSON, - validations: [], - }, - savedQueryId: { - type: FIELD_TYPES.TEXT, - validations: [], - }, - query: { - defaultValue: '', - type: FIELD_TYPES.TEXT, - validations: [ - { - validator: fieldValidators.maxLengthField({ - length: MAX_QUERY_LENGTH, - message: i18n.translate('xpack.osquery.liveQuery.queryForm.largeQueryError', { - defaultMessage: 'Query is too large (max {maxLength} characters)', - values: { maxLength: MAX_QUERY_LENGTH }, - }), - }), - }, - { validator: queryFieldValidation }, - ], - }, - packId: { - label: i18n.translate('xpack.osquery.packs.dropdown.searchFieldLabel', { - defaultMessage: `Pack`, - }), - type: FIELD_TYPES.COMBO_BOX, - defaultValue: [], - }, - ecs_mapping: { - defaultValue: [], - type: FIELD_TYPES.JSON, - }, -}; diff --git a/x-pack/plugins/osquery/public/live_queries/index.tsx b/x-pack/plugins/osquery/public/live_queries/index.tsx index 22aa3be77c2a7..746687f4b644b 100644 --- a/x-pack/plugins/osquery/public/live_queries/index.tsx +++ b/x-pack/plugins/osquery/public/live_queries/index.tsx @@ -10,6 +10,7 @@ import { EuiCode, EuiLoadingContent, EuiEmptyPrompt } from '@elastic/eui'; import React, { useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; +import type { EcsMappingSerialized } from '../packs/queries/ecs_mapping_editor_field'; import { LiveQueryForm } from './form'; import { useActionResultsPrivileges } from '../action_results/use_action_privileges'; import { OSQUERY_INTEGRATION_NAME } from '../../common'; @@ -23,7 +24,7 @@ interface LiveQueryProps { onSuccess?: () => void; query?: string; savedQueryId?: string; - ecs_mapping?: unknown; + ecs_mapping?: EcsMappingSerialized; agentsField?: boolean; queryField?: boolean; ecsMappingField?: boolean; diff --git a/x-pack/plugins/osquery/public/live_queries/use_create_live_query_action.tsx b/x-pack/plugins/osquery/public/live_queries/use_create_live_query_action.tsx index 72ac627b02a63..dfb08ddeda87d 100644 --- a/x-pack/plugins/osquery/public/live_queries/use_create_live_query_action.tsx +++ b/x-pack/plugins/osquery/public/live_queries/use_create_live_query_action.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { useMutation } from 'react-query'; +import { useMutation } from '@tanstack/react-query'; import type { AgentSelection } from '../../common/schemas/common'; import type { CreateLiveQueryRequestBodySchema } from '../../common/schemas/routes/live_query'; import { useKibana } from '../common/lib/kibana'; diff --git a/x-pack/plugins/osquery/public/packs/active_state_switch.tsx b/x-pack/plugins/osquery/public/packs/active_state_switch.tsx index 41f7c7a32522e..cb67f248207fc 100644 --- a/x-pack/plugins/osquery/public/packs/active_state_switch.tsx +++ b/x-pack/plugins/osquery/public/packs/active_state_switch.tsx @@ -7,7 +7,7 @@ import { EuiSwitch, EuiLoadingSpinner } from '@elastic/eui'; import React, { useCallback, useMemo, useState } from 'react'; -import { useQueryClient } from 'react-query'; +import { useQueryClient } from '@tanstack/react-query'; import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; @@ -55,7 +55,7 @@ const ActiveStateSwitchComponent: React.FC = ({ item }) const { isLoading, mutateAsync } = useUpdatePack({ options: { onSuccess: (response) => { - queryClient.invalidateQueries(PACKS_ID); + queryClient.invalidateQueries([PACKS_ID]); setErrorToast(); toasts.addSuccess( response?.data?.attributes.enabled diff --git a/x-pack/plugins/osquery/public/packs/form/queries_field.tsx b/x-pack/plugins/osquery/public/packs/form/queries_field.tsx index bff3e30a901c1..62f5c9c7e7e93 100644 --- a/x-pack/plugins/osquery/public/packs/form/queries_field.tsx +++ b/x-pack/plugins/osquery/public/packs/form/queries_field.tsx @@ -93,6 +93,7 @@ const QueriesFieldComponent: React.FC = ({ if (updatedQuery.ecs_mapping) { draft[showEditQueryFlyout].ecs_mapping = updatedQuery.ecs_mapping; } else { + // @ts-expect-error update types delete draft[showEditQueryFlyout].ecs_mapping; } @@ -231,6 +232,7 @@ const QueriesFieldComponent: React.FC = ({ {showEditQueryFlyout != null && showEditQueryFlyout >= 0 && ( ; const typeMap = { binary: 'binary', @@ -80,17 +85,6 @@ const typeMap = { constant_keyword: 'string', }; -const StyledEuiSuperSelect = styled(EuiSuperSelect)` - min-width: 70px; - border-radius: 6px 0 0 6px; - - .euiIcon { - padding: 0; - width: 18px; - background: none; - } -`; - // @ts-expect-error update types const ResultComboBox = styled(EuiComboBox)` &.euiComboBox { @@ -103,6 +97,17 @@ const ResultComboBox = styled(EuiComboBox)` } `; +const StyledEuiSuperSelect = styled(EuiSuperSelect)` + min-width: 70px; + border-radius: 6px 0 0 6px; + + .euiIcon { + padding: 0; + width: 18px; + background: none; + } +`; + const StyledFieldIcon = styled(FieldIcon)` width: 32px; @@ -144,31 +149,32 @@ const ECSSchemaOptions = ECSSchema.map((ecs) => ({ type ECSSchemaOption = typeof ECSSchemaOptions[0]; -interface ECSComboboxFieldProps { - field: FieldHook; +interface ECSComboboxFieldProps extends FormField { euiFieldProps: EuiComboBoxProps; idAria?: string; + error?: string; } const ECSComboboxFieldComponent: React.FC = ({ - field, euiFieldProps = {}, idAria, + onChange, + value, + error, }) => { - const { setValue } = field; const [selectedOptions, setSelected] = useState>>( [] ); - const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field); const describedByIds = useMemo(() => (idAria ? [idAria] : []), [idAria]); - const [formData] = useFormData(); - + const { ecs_mapping: watchedEcsMapping } = useWatch() as unknown as { + ecs_mapping: EcsMappingFormField[]; + }; const handleChange = useCallback( (newSelectedOptions) => { setSelected(newSelectedOptions); - setValue(newSelectedOptions[0]?.label ?? ''); + onChange(newSelectedOptions[0]?.label ?? ''); }, - [setValue] + [onChange] ); // TODO: Create own component for this. @@ -230,37 +236,36 @@ const ECSComboboxFieldComponent: React.FC = ({ }, [selectedOptions]); const availableECSSchemaOptions = useMemo(() => { - const currentFormECSFieldValues = map(formData.ecs_mapping, 'key'); + const currentFormECSFieldValues = map(watchedEcsMapping, 'key'); return ECSSchemaOptions.filter(({ label }) => !currentFormECSFieldValues.includes(label)); - }, [formData.ecs_mapping]); + }, [watchedEcsMapping]); useEffect(() => { // @ts-expect-error update types setSelected(() => { - if (!field.value.length) return []; + if (!value?.length) return []; - const selectedOption = find(ECSSchemaOptions, ['label', field.value]); + const selectedOption = find(ECSSchemaOptions, ['label', value]); return selectedOption ? [selectedOption] : [ { - label: field.value, + label: value, value: { - value: field.value, + value, }, }, ]; }); - }, [field.value]); + }, [value]); return ( ; - resultValue: FieldHook; euiFieldProps: EuiComboBoxProps; - item: ArrayItem; + item: EcsMappingFormField; + index: number; idAria?: string; + isLastItem: boolean; } const OsqueryColumnFieldComponent: React.FC = ({ - resultType, - resultValue, - euiFieldProps = {}, + euiFieldProps, idAria, item, + index, + isLastItem, }) => { + const osqueryResultFieldValidator = ( + value: string, + ecsMappingFormData: EcsMappingFormField[] + ): string | undefined => { + const currentMapping = ecsMappingFormData[index]; + + if (!value.length && currentMapping.key.length) { + return i18n.translate( + 'xpack.osquery.pack.queryFlyoutForm.osqueryResultFieldRequiredErrorMessage', + { + defaultMessage: 'Value field is required.', + } + ); + } + + if (!value.length || currentMapping.result.type !== 'field') return; + + const osqueryColumnExists = find(euiFieldProps.options, [ + 'label', + isArray(value) ? value[0] : value, + ]); + + return !osqueryColumnExists + ? i18n.translate( + 'xpack.osquery.pack.queryFlyoutForm.osqueryResultFieldValueMissingErrorMessage', + { + defaultMessage: 'The current query does not return a {columnName} field', + values: { + columnName: value, + }, + } + ) + : undefined; + }; + + const { setValue } = useFormContext(); + const { ecs_mapping: watchedEcsMapping } = useWatch() as unknown as { + ecs_mapping: EcsMappingFormField[]; + }; + + const { field: resultField, fieldState: resultFieldState } = useController({ + name: `ecs_mapping.${index}.result.value`, + rules: { + validate: (data) => osqueryResultFieldValidator(data, watchedEcsMapping), + }, + defaultValue: '', + }); + const itemPath = `ecs_mapping.${index}`; + const resultValue = item.result; const inputRef = useRef(); - const { setValue } = resultValue; - const { value: typeValue, setValue: setType } = resultType; - const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(resultValue); + const [selectedOptions, setSelected] = useState([]); const describedByIds = useMemo(() => (idAria ? [idAria] : []), [idAria]); - const [selectedOptions, setSelected] = useState< - Array> - >([]); - const [formData] = useFormData(); const renderOsqueryOption = useCallback( (option, searchValue, contentClassName) => ( @@ -365,7 +413,6 @@ const OsqueryColumnFieldComponent: React.FC = ({ {option.value.suggestion_label}
- {option.value.description} @@ -376,37 +423,44 @@ const OsqueryColumnFieldComponent: React.FC = ({ [] ); - const handleChange = useCallback( + const handleKeyChange = useCallback( (newSelectedOptions) => { setSelected(newSelectedOptions); - setValue( + resultField.onChange( isArray(newSelectedOptions) ? map(newSelectedOptions, 'label') : newSelectedOptions[0]?.label ?? '' ); }, - [setValue, setSelected] + [resultField] ); const isSingleSelection = useMemo(() => { - const ecsKey = get(formData, item.path)?.key; - if (ecsKey?.length && typeValue === 'value') { - const ecsKeySchemaOption = find(ECSSchemaOptions, ['label', ecsKey]); + const ecsData = get(watchedEcsMapping, `${index}`); + if (ecsData?.key?.length && item.result.type === 'value') { + const ecsKeySchemaOption = find(ECSSchemaOptions, ['label', ecsData?.key]); return ecsKeySchemaOption?.value?.normalization !== 'array'; } - return !!ecsKey?.length; - }, [typeValue, formData, item.path]); + if (!ecsData?.key?.length && isLastItem) { + return true; + } + + return !!ecsData?.key?.length; + }, [index, isLastItem, item.result.type, watchedEcsMapping]); const onTypeChange = useCallback( (newType) => { - if (newType !== typeValue) { - setType(newType); - setValue(newType === 'value' && isSingleSelection === false ? [] : ''); + if (newType !== item.result.type) { + setValue(`${itemPath}.result.type`, newType); + setValue( + `${itemPath}.result.value`, + newType === 'value' && isSingleSelection === false ? [] : '' + ); } }, - [typeValue, setType, setValue, isSingleSelection] + [isSingleSelection, item.result.type, itemPath, setValue] ); const handleCreateOption = useCallback( @@ -416,19 +470,19 @@ const OsqueryColumnFieldComponent: React.FC = ({ if (!trimmedNewOption.length) return; if (isSingleSelection === false) { - setValue([trimmedNewOption]); - if (resultValue.value.length) { - setValue([...castArray(resultValue.value), trimmedNewOption]); + setValue(`${itemPath}.result.value`, [trimmedNewOption]); + if (item.result.value.length) { + setValue(`${itemPath}.result.value`, [...castArray(resultValue.value), trimmedNewOption]); } else { - setValue([trimmedNewOption]); + setValue(`${itemPath}.result.value`, [trimmedNewOption]); } inputRef.current?.blur(); } else { - setValue(trimmedNewOption); + setValue(`${itemPath}.result.value`, trimmedNewOption); } }, - [isSingleSelection, resultValue.value, setValue] + [isSingleSelection, item.result.value.length, itemPath, resultValue.value, setValue] ); const Prepend = useMemo( @@ -436,7 +490,7 @@ const OsqueryColumnFieldComponent: React.FC = ({ = ({ onChange={onTypeChange} /> ), - [euiFieldProps.isDisabled, onTypeChange, typeValue] + [euiFieldProps.isDisabled, item.result.type, onTypeChange] ); useEffect(() => { if (isSingleSelection && isArray(resultValue.value)) { - setValue(resultValue.value.join(' ')); + setValue(`${itemPath}.result.value`, resultValue.value.join(' ')); } if (!isSingleSelection && !isArray(resultValue.value)) { - setValue(resultValue.value.length ? [resultValue.value] : []); + const value = resultValue.value.length ? [resultValue.value] : []; + setValue(`${itemPath}.result.value`, value); } - }, [isSingleSelection, resultValue.value, setValue]); + }, [index, isSingleSelection, itemPath, resultValue, resultValue.value, setValue]); useEffect(() => { - setSelected(() => { + // @ts-expect-error hard to type to satisfy TS, but it represents proper types + setSelected((_: OsquerySchemaOption[]): OsquerySchemaOption[] | Array<{ label: string }> => { if (!resultValue.value.length) return []; // Static array values if (isArray(resultValue.value)) { - return resultValue.value.map((value) => ({ label: value })); + return resultValue.value.map((value) => ({ label: value })) as OsquerySchemaOption[]; } - const selectedOption = find(euiFieldProps?.options, ['label', resultValue.value]); + const selectedOption = find(euiFieldProps?.options, ['label', resultValue.value]) as + | OsquerySchemaOption + | undefined; return selectedOption ? [selectedOption] : [{ label: resultValue.value }]; }); @@ -476,10 +534,9 @@ const OsqueryColumnFieldComponent: React.FC = ({ return ( = ({ {Prepend} { inputRef.current = ref; }} fullWidth selectedOptions={selectedOptions} - onChange={handleChange} + onChange={handleKeyChange} onCreateOption={handleCreateOption} renderOption={renderOsqueryOption} rowHeight={32} isClearable - {...euiFieldProps} singleSelection={isSingleSelection ? SINGLE_SELECTION : false} - options={(typeValue === 'field' && euiFieldProps.options) || EMPTY_ARRAY} + options={(item.result.type === 'field' && euiFieldProps.options) || EMPTY_ARRAY} + idAria={idAria} + helpText={selectedOptions[0]?.value?.description} + {...euiFieldProps} /> @@ -518,177 +581,76 @@ export interface ECSMappingEditorFieldProps { interface ECSMappingEditorFormProps { isDisabled?: boolean; osquerySchemaOptions: OsquerySchemaOption[]; - item: ArrayItem; - isLastItem?: boolean; + item: EcsMappingFormField; + index: number; + isLastItem: boolean; + onAppend: (ecs_mapping: EcsMappingFormField[]) => void; onDelete?: FormArrayField['removeItem']; } -const ecsFieldValidator = ( - args: ValidationFuncArg & { - customData: { - value: { - editForm: boolean; - }; - }; - } -) => { - const editForm: boolean = args.customData.value?.editForm; - const rootPath = args.path.split('.')[0]; - - const fieldRequiredError = fieldValidators.emptyField( - i18n.translate('xpack.osquery.pack.queryFlyoutForm.ecsFieldRequiredErrorMessage', { - defaultMessage: 'ECS field is required.', - }) - )(args); - - if ( - fieldRequiredError && - // @ts-expect-error update types - ((!editForm && args.formData[`${rootPath}.result.value`]?.length) || editForm) - ) { - return fieldRequiredError; - } - - return undefined; -}; - -const osqueryResultFieldValidator = async ( - args: ValidationFuncArg & { - customData: { - value: { - editForm: boolean; - osquerySchemaOptions: OsquerySchemaOption[]; - }; - }; - } -) => { - const rootPath = args.path.split('.')[0]; - const { editForm, osquerySchemaOptions } = args.customData.value; - const fieldRequiredError = fieldValidators.emptyField( - i18n.translate('xpack.osquery.pack.queryFlyoutForm.osqueryResultFieldRequiredErrorMessage', { - defaultMessage: 'Value is required.', - }) - )(args); - - // @ts-expect-error update types - if (fieldRequiredError && ((!editForm && args.formData[`${rootPath}.key`]?.length) || editForm)) { - return fieldRequiredError; - } - - // @ts-expect-error update types - if (!args.value?.length || args.formData[`${rootPath}.result.type`] !== 'field') return; - - const osqueryColumnExists = find(osquerySchemaOptions, [ - 'label', - isArray(args.value) ? args.value[0] : args.value, - ]); - - return !osqueryColumnExists - ? { - code: 'ERR_FIELD_FORMAT', - path: args.path, - message: i18n.translate( - 'xpack.osquery.pack.queryFlyoutForm.osqueryResultFieldValueMissingErrorMessage', - { - defaultMessage: 'The current query does not return a {columnName} field', - values: { - columnName: args.value, - }, - } - ), - } - : undefined; +export const defaultEcsFormData = { + key: '', + result: { + type: 'field', + value: '', + }, }; -interface ECSMappingEditorFormData { - key: string; - value: { - field?: string; - value?: string; - }; -} - export const ECSMappingEditorForm: React.FC = ({ isDisabled, osquerySchemaOptions, item, isLastItem, + index, onDelete, }) => { + const ecsFieldValidator = (value: string, ecsMapping: EcsMappingFormField[]) => { + const ecsCurrentMapping = ecsMapping[index].result.value; + + return !value.length && ecsCurrentMapping.length + ? i18n.translate('xpack.osquery.pack.queryFlyoutForm.ecsFieldRequiredErrorMessage', { + defaultMessage: 'ECS field is required.', + }) + : undefined; + }; + + const { ecs_mapping: ecsMapping } = useWatch() as unknown as { + ecs_mapping: EcsMappingFormField[]; + }; + const { field: ECSField, fieldState: ECSFieldState } = useController({ + name: `ecs_mapping.${index}.key`, + rules: { + validate: (value: string) => ecsFieldValidator(value, ecsMapping), + }, + defaultValue: '', + }); + const MultiFields = useMemo( () => ( - - {(fields) => ( - - )} - +
+ +
), - [item, osquerySchemaOptions, isLastItem, isDisabled] + [item, index, isLastItem, osquerySchemaOptions, isDisabled] ); const ecsComboBoxEuiFieldProps = useMemo(() => ({ isDisabled }), [isDisabled]); - const validationData = useMemo(() => ({ editForm: !isLastItem }), [isLastItem]); - - const config = useMemo( - () => ({ - valueChangeDebounceTime: 300, - fieldsToValidateOnChange: [`${item.path}.key`, `${item.path}.result.value`], - validations: [ - { - validator: ecsFieldValidator, - }, - ], - }), - [item.path] - ); - const handleDeleteClick = useCallback(() => { if (onDelete) { - onDelete(item.id); + onDelete(index); } - }, [item.id, onDelete]); + }, [index, onDelete]); return ( <> @@ -696,14 +658,13 @@ export const ECSMappingEditorForm: React.FC = ({ - @@ -764,22 +725,26 @@ interface OsqueryColumn { export const ECSMappingEditorField = React.memo( ({ euiFieldProps }: ECSMappingEditorFieldProps) => { - const lastItemPath = useRef(); - const onAdd = useRef(); - const itemsList = useRef([]); - const [osquerySchemaOptions, setOsquerySchemaOptions] = useState([]); - const [{ query, ...formData }, formDataSerializer, isMounted] = useFormData(); + const { trigger } = useFormContext(); + const { fields, append, remove } = useFieldArray<{ ecs_mapping: EcsMappingFormField[] }>({ + name: 'ecs_mapping', + }); - const { validateFields } = useFormContext(); + const itemsList = useRef>([]); + const [osquerySchemaOptions, setOsquerySchemaOptions] = useState([]); + const { query, ...formData } = useWatch() as unknown as { + query: string; + ecs_mapping: EcsMappingFormField[]; + }; useEffect(() => { // Additional 'suspended' validation of osquery ecs fields. fieldsToValidateOnChange doesn't work because it happens before the osquerySchema gets updated. - const fieldsToValidate = prepareEcsFieldsToValidate(itemsList.current); + const fieldsToValidate = prepareEcsFieldsToValidate(fields); // it is always at least 2 - empty fields if (fieldsToValidate.length > 2) { - setTimeout(() => validateFields(fieldsToValidate), 0); + setTimeout(async () => await trigger('ecs_mapping'), 0); } - }, [query, validateFields]); + }, [fields, query, trigger]); useEffect(() => { if (!query?.length) { @@ -1013,32 +978,23 @@ export const ECSMappingEditorField = React.memo( }, [query]); useLayoutEffect(() => { - if (isMounted) { - if (!lastItemPath.current && onAdd.current) { - onAdd.current(); - - return; - } - - if (euiFieldProps?.isDisabled) { - return; - } - - const itemKey = get(formData, `${lastItemPath.current}.key`); + const ecsList = formData?.ecs_mapping; + const lastEcs = formData?.ecs_mapping?.[itemsList?.current.length - 1]; - if (itemKey) { - const serializedFormData = formDataSerializer(); - const itemValue = - serializedFormData.ecs_mapping && - (serializedFormData.ecs_mapping[`${itemKey}`]?.field || - serializedFormData.ecs_mapping[`${itemKey}`]?.value); + // we skip appending on remove + if (itemsList?.current?.length < ecsList?.length) { + return; + } - if (itemValue && onAdd.current) { - onAdd.current(); - } - } + // // list contains ecs already, and the last item has values provided + if ( + ecsList?.length === itemsList.current.length && + lastEcs?.key?.length && + lastEcs?.result?.value?.length + ) { + return append(defaultEcsFormData); } - }, [euiFieldProps?.isDisabled, formData, formDataSerializer, isMounted, onAdd]); + }, [append, euiFieldProps?.isDisabled, formData]); return ( <> @@ -1080,28 +1036,24 @@ export const ECSMappingEditorField = React.memo( - - {({ items, addItem, removeItem }) => { - lastItemPath.current = items[items.length - 1]?.path; - onAdd.current = addItem; - itemsList.current = items; - - return ( - <> - {items.map((item, index) => ( - - ))} - - ); - }} - + + {fields.map((item, index, array) => { + itemsList.current = array; + + return ( +
+ +
+ ); + })} ); }, diff --git a/x-pack/plugins/osquery/public/packs/queries/platform_checkbox_group_field.tsx b/x-pack/plugins/osquery/public/packs/queries/platform_checkbox_group_field.tsx index 1aa83a39f12b7..62e0a7bdaef6d 100644 --- a/x-pack/plugins/osquery/public/packs/queries/platform_checkbox_group_field.tsx +++ b/x-pack/plugins/osquery/public/packs/queries/platform_checkbox_group_field.tsx @@ -11,23 +11,22 @@ import type { EuiCheckboxGroupOption } from '@elastic/eui'; import { EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiCheckboxGroup } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { FieldHook } from '../../shared_imports'; -import { getFieldValidityAndErrorMessage } from '../../shared_imports'; +import { useController } from 'react-hook-form'; +import { i18n } from '@kbn/i18n'; +import type { FormFieldProps } from '../../form/types'; import { PlatformIcon } from './platforms/platform_icon'; -interface Props { - field: FieldHook; - euiFieldProps?: Record; - idAria?: string; - [key: string]: unknown; -} +type Props = Omit, 'name' | 'label'>; -export const PlatformCheckBoxGroupField = ({ - field, - euiFieldProps = {}, - idAria, - ...rest -}: Props) => { +export const PlatformCheckBoxGroupField = (props: Props) => { + const { euiFieldProps = {}, idAria, helpText, ...rest } = props; + const { + field: { onChange, value }, + fieldState: { error }, + } = useController({ + name: 'platform', + defaultValue: [], + }); const options = useMemo( () => [ { @@ -82,17 +81,16 @@ export const PlatformCheckBoxGroupField = ({ [] ); - const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field); const [checkboxIdToSelectedMap, setCheckboxIdToSelectedMap] = useState>( () => (options as EuiCheckboxGroupOption[]).reduce((acc, option) => { - acc[option.id] = isEmpty(field.value) ? true : field.value?.includes(option.id) ?? false; + acc[option.id] = isEmpty(value) ? true : value?.includes(option.id) ?? false; return acc; }, {} as Record) ); - const onChange = useCallback( + const handleChange = useCallback( (optionId: string) => { const newCheckboxIdToSelectedMap = { ...checkboxIdToSelectedMap, @@ -100,11 +98,13 @@ export const PlatformCheckBoxGroupField = ({ }; setCheckboxIdToSelectedMap(newCheckboxIdToSelectedMap); - field.setValue(() => - Object.keys(pickBy(newCheckboxIdToSelectedMap, (value) => value === true)).join(',') + onChange( + Object.keys( + pickBy(newCheckboxIdToSelectedMap, (checkboxValue) => checkboxValue === true) + ).join(',') ); }, - [checkboxIdToSelectedMap, field] + [checkboxIdToSelectedMap, onChange] ); const describedByIds = useMemo(() => (idAria ? [idAria] : []), [idAria]); @@ -112,19 +112,23 @@ export const PlatformCheckBoxGroupField = ({ useEffect(() => { setCheckboxIdToSelectedMap(() => (options as EuiCheckboxGroupOption[]).reduce((acc, option) => { - acc[option.id] = isEmpty(field.value) ? true : field.value?.includes(option.id) ?? false; + acc[option.id] = isEmpty(value) ? true : value?.includes(option.id) ?? false; return acc; }, {} as Record) ); - }, [field.value, options]); + }, [value, options]); + + const hasError = useMemo(() => !!error?.message, [error?.message]); return ( diff --git a/x-pack/plugins/osquery/public/packs/queries/query_flyout.tsx b/x-pack/plugins/osquery/public/packs/queries/query_flyout.tsx index 72317dbadc708..3d8872cb45d64 100644 --- a/x-pack/plugins/osquery/public/packs/queries/query_flyout.tsx +++ b/x-pack/plugins/osquery/public/packs/queries/query_flyout.tsx @@ -5,7 +5,6 @@ * 2.0. */ -import { map } from 'lodash'; import { EuiFlyout, EuiTitle, @@ -17,28 +16,33 @@ import { EuiFlexItem, EuiButtonEmpty, EuiButton, - EuiText, } from '@elastic/eui'; import React, { useCallback, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { FormProvider } from 'react-hook-form'; +import { isEmpty, map } from 'lodash'; +import { QueryIdField, IntervalField } from '../../form'; +import { defaultEcsFormData } from './ecs_mapping_editor_field'; import { CodeEditorField } from '../../saved_queries/form/code_editor_field'; -import { Form, getUseField, Field } from '../../shared_imports'; import { PlatformCheckBoxGroupField } from './platform_checkbox_group_field'; import { ALL_OSQUERY_VERSIONS_OPTIONS } from './constants'; -import type { UsePackQueryFormProps, PackQueryFormData } from './use_pack_query_form'; +import type { + UsePackQueryFormProps, + PackQueryFormData, + PackSOQueryFormData, +} from './use_pack_query_form'; import { usePackQueryForm } from './use_pack_query_form'; import { SavedQueriesDropdown } from '../../saved_queries/saved_queries_dropdown'; import { ECSMappingEditorField } from './lazy_ecs_mapping_editor_field'; import { useKibana } from '../../common/lib/kibana'; - -const CommonUseField = getUseField({ component: Field }); +import { VersionField } from '../../form'; interface QueryFlyoutProps { uniqueQueryIds: string[]; defaultValue?: UsePackQueryFormProps['defaultValue'] | undefined; - onSave: (payload: PackQueryFormData) => Promise; + onSave: (payload: PackSOQueryFormData) => void; onClose: () => void; } @@ -50,45 +54,48 @@ const QueryFlyoutComponent: React.FC = ({ }) => { const permissions = useKibana().services.application.capabilities.osquery; const [isEditMode] = useState(!!defaultValue); - const { form } = usePackQueryForm({ + const { serializer, idSet, ...hooksForm } = usePackQueryForm({ uniqueQueryIds, defaultValue, - handleSubmit: async (payload, isValid) => - new Promise((resolve) => { - if (isValid) { - onSave(payload); - onClose(); - } - - resolve(); - }), }); - const { submit, isSubmitting, updateFieldValues } = form; + const { + handleSubmit, + formState: { isSubmitting }, + setValue, + clearErrors, + } = hooksForm; + const onSubmit = (payload: PackQueryFormData) => { + const serializedData: PackSOQueryFormData = serializer(payload); + onSave(serializedData); + onClose(); + }; const handleSetQueryValue = useCallback( (savedQuery) => { if (savedQuery) { - updateFieldValues({ - id: savedQuery.id, - query: savedQuery.query, - description: savedQuery.description, - platform: savedQuery.platform ? savedQuery.platform : 'linux,windows,darwin', - version: savedQuery.version, - interval: savedQuery.interval, - // @ts-expect-error update types - ecs_mapping: - map(savedQuery.ecs_mapping, (value, key) => ({ - key, - result: { - type: Object.keys(value)[0], - value: Object.values(value)[0], - }, - })) ?? [], - }); + clearErrors('id'); + setValue('id', savedQuery.id); + setValue('query', savedQuery.query); + // setValue('description', savedQuery.description); // TODO do we need it? + setValue('platform', savedQuery.platform ? savedQuery.platform : 'linux,windows,darwin'); + setValue('version', savedQuery.version ? [savedQuery.version] : []); + setValue('interval', savedQuery.interval); + setValue( + 'ecs_mapping', + !isEmpty(savedQuery.ecs_mapping) + ? map(savedQuery.ecs_mapping, (value, key) => ({ + key, + result: { + type: Object.keys(value)[0], + value: Object.values(value)[0] as string, + }, + })) + : [defaultEcsFormData] + ); } }, - [updateFieldValues] + [clearErrors, setValue] ); /* Avoids accidental closing of the flyout when the user clicks outside of the flyout */ const maskProps = useMemo(() => ({ onClick: () => ({}) }), []); @@ -119,37 +126,25 @@ const QueryFlyoutComponent: React.FC = ({ -
+ {!isEditMode && permissions.readSavedQueries ? ( <> ) : null} - + - + - - - - - - - } + = ({ /> - + @@ -172,7 +167,7 @@ const QueryFlyoutComponent: React.FC = ({ - +
@@ -185,7 +180,7 @@ const QueryFlyoutComponent: React.FC = ({
- + ) => ({ - id: { - type: FIELD_TYPES.TEXT, - label: i18n.translate('xpack.osquery.pack.queryFlyoutForm.idFieldLabel', { - defaultMessage: 'ID', - }), - validations: createIdFieldValidations(ids).map((validator) => ({ validator })), - }, - description: { - type: FIELD_TYPES.TEXT, - label: i18n.translate('xpack.osquery.pack.queryFlyoutForm.descriptionFieldLabel', { - defaultMessage: 'Description (optional)', - }), - validations: [], - }, - query: { - type: FIELD_TYPES.TEXT, - label: i18n.translate('xpack.osquery.pack.queryFlyoutForm.queryFieldLabel', { - defaultMessage: 'Query', - }), - validations: [{ validator: queryFieldValidation }], - }, - interval: { - defaultValue: 3600, - type: FIELD_TYPES.NUMBER, - label: i18n.translate('xpack.osquery.pack.queryFlyoutForm.intervalFieldLabel', { - defaultMessage: 'Interval (s)', - }), - validations: intervalFieldValidations, - }, - platform: { - type: FIELD_TYPES.TEXT, - label: i18n.translate('xpack.osquery.pack.queryFlyoutForm.platformFieldLabel', { - defaultMessage: 'Platform', - }), - validations: [], - }, - version: { - defaultValue: [], - type: FIELD_TYPES.COMBO_BOX, - label: ( - - - - - - ) as unknown as string, - validations: [], - }, - ecs_mapping: { - defaultValue: [], - type: FIELD_TYPES.JSON, - }, -}); diff --git a/x-pack/plugins/osquery/public/packs/queries/use_pack_query_form.tsx b/x-pack/plugins/osquery/public/packs/queries/use_pack_query_form.tsx index 16da80b048921..4a1e47c3df9b7 100644 --- a/x-pack/plugins/osquery/public/packs/queries/use_pack_query_form.tsx +++ b/x-pack/plugins/osquery/public/packs/queries/use_pack_query_form.tsx @@ -5,134 +5,113 @@ * 2.0. */ -import { isArray, isEmpty, xor, map } from 'lodash'; -import uuid from 'uuid'; -import { produce } from 'immer'; +import { isArray, isEmpty, map, xor } from 'lodash'; +import { useForm as useHookForm } from 'react-hook-form'; +import type { Draft } from 'immer'; +import { produce } from 'immer'; import { useMemo } from 'react'; -import type { ECSMapping } from '../../../common/schemas/common'; import { convertECSMappingToObject } from '../../../common/schemas/common/utils'; -import type { FormConfig } from '../../shared_imports'; -import { useForm } from '../../shared_imports'; -import { createFormSchema } from './schema'; - -const FORM_ID = 'editQueryFlyoutForm'; +import type { EcsMappingFormField } from './ecs_mapping_editor_field'; +import { defaultEcsFormData } from './ecs_mapping_editor_field'; export interface UsePackQueryFormProps { uniqueQueryIds: string[]; - defaultValue?: PackQueryFormData | undefined; - handleSubmit: FormConfig['onSubmit']; + defaultValue?: PackSOQueryFormData | undefined; } export interface PackSOQueryFormData { id: string; query: string; - interval: number; + interval: string; platform?: string | undefined; version?: string | undefined; - ecs_mapping?: PackQuerySOECSMapping[] | undefined; + ecs_mapping?: PackQuerySOECSMapping[]; } export type PackQuerySOECSMapping = Array<{ field: string; value: string }>; export interface PackQueryFormData { - id?: string; + id: string; description?: string; query: string; - interval?: number; + interval: number; platform?: string | undefined; - version?: string | undefined; - ecs_mapping?: ECSMapping; + version?: string[] | undefined; + ecs_mapping: EcsMappingFormField[]; } -export type PackQueryECSMapping = Record< - string, - { - field?: string; - value?: string; - } ->; - -export const usePackQueryForm = ({ - uniqueQueryIds, - defaultValue, - handleSubmit, -}: UsePackQueryFormProps) => { - const idSet = useMemo>( - () => new Set(xor(uniqueQueryIds, defaultValue?.id ? [defaultValue.id] : [])), - [uniqueQueryIds, defaultValue] - ); - const formSchema = useMemo>( - () => createFormSchema(idSet), - [idSet] - ); +const deserializer = (payload: PackSOQueryFormData): PackQueryFormData => + ({ + id: payload.id, + query: payload.query, + interval: payload.interval ? parseInt(payload.interval, 10) : 3600, + platform: payload.platform, + version: payload.version ? [payload.version] : [], + ecs_mapping: !isEmpty(payload.ecs_mapping) + ? !isArray(payload.ecs_mapping) + ? map(payload.ecs_mapping as unknown as PackQuerySOECSMapping, (value, key) => ({ + key, + result: { + type: Object.keys(value)[0], + value: Object.values(value)[0], + }, + })) + : payload.ecs_mapping + : [defaultEcsFormData], + } as PackQueryFormData); - return useForm({ - id: FORM_ID + uuid.v4(), - onSubmit: async (formData, isValid) => { - if (isValid && handleSubmit) { - // @ts-expect-error update types - return handleSubmit(formData, isValid); +const serializer = (payload: PackQueryFormData): PackSOQueryFormData => + // @ts-expect-error update types + produce(payload, (draft: Draft) => { + if (isArray(draft.platform)) { + if (draft.platform.length) { + draft.platform.join(','); + } else { + delete draft.platform; } - }, - options: { - stripEmptyFields: true, - }, - // @ts-expect-error update types - defaultValue: defaultValue || { - id: '', - query: '', - interval: 3600, - ecs_mapping: [], - }, - // @ts-expect-error update types - serializer: (payload) => - produce(payload, (draft) => { - if (isArray(draft.platform)) { - draft.platform.join(','); - } + } - if (isArray(draft.version)) { - if (!draft.version.length) { - delete draft.version; - } else { - draft.version = draft.version[0]; - } - } + if (isArray(draft.version)) { + if (!draft.version.length) { + delete draft.version; + } else { + draft.version = draft.version[0]; + } + } - if (isEmpty(draft.ecs_mapping)) { - delete draft.ecs_mapping; - } else { - // @ts-expect-error update types - draft.ecs_mapping = convertECSMappingToObject(payload.ecs_mapping); - } + if (draft.interval) { + draft.interval = draft.interval + ''; + } - return draft; - }), - // @ts-expect-error update types - deserializer: (payload) => { - if (!payload) return {} as PackQueryFormData; + if (isEmpty(draft.ecs_mapping)) { + delete draft.ecs_mapping; + } else { + // @ts-expect-error update types + draft.ecs_mapping = convertECSMappingToObject(payload.ecs_mapping); + } - return { - id: payload.id, - query: payload.query, - interval: payload.interval, - platform: payload.platform, - version: payload.version ? [payload.version] : [], - ecs_mapping: !isArray(payload.ecs_mapping) - ? map(payload.ecs_mapping, (value, key) => ({ - key, - result: { - // @ts-expect-error update types - type: Object.keys(value)[0], - // @ts-expect-error update types - value: Object.values(value)[0], - }, - })) - : payload.ecs_mapping, - }; - }, - // @ts-expect-error update types - schema: formSchema, + return draft; }); + +export const usePackQueryForm = ({ uniqueQueryIds, defaultValue }: UsePackQueryFormProps) => { + const idSet = useMemo>( + () => new Set(xor(uniqueQueryIds, defaultValue?.id ? [defaultValue.id] : [])), + [uniqueQueryIds, defaultValue] + ); + + return { + serializer, + idSet, + ...useHookForm({ + defaultValues: defaultValue + ? deserializer(defaultValue) + : { + id: '', + query: '', + interval: 3600, + ecs_mapping: [defaultEcsFormData], + }, + }), + }; }; diff --git a/x-pack/plugins/osquery/public/packs/queries/validations.ts b/x-pack/plugins/osquery/public/packs/queries/validations.ts index 54ea9deece692..37d74806fd1ae 100644 --- a/x-pack/plugins/osquery/public/packs/queries/validations.ts +++ b/x-pack/plugins/osquery/public/packs/queries/validations.ts @@ -6,12 +6,11 @@ */ import { i18n } from '@kbn/i18n'; +import type { FormData, ValidationFunc } from '../../shared_imports'; -import type { FormData, ValidationConfig, ValidationFunc } from '../../shared_imports'; -import { fieldValidators } from '../../shared_imports'; -export { queryFieldValidation } from '../../common/validations'; - +export const MAX_QUERY_LENGTH = 2000; const idPattern = /^[a-zA-Z0-9-_]+$/; +// still used in Packs export const idSchemaValidation: ValidationFunc = ({ value }) => { const valueIsValid = idPattern.test(value); if (!valueIsValid) { @@ -23,47 +22,39 @@ export const idSchemaValidation: ValidationFunc = ({ v } }; +export const idHookSchemaValidation = (value: string) => { + const valueIsValid = idPattern.test(value); + + if (!valueIsValid) { + return i18n.translate('xpack.osquery.pack.queryFlyoutForm.invalidIdError', { + defaultMessage: 'Characters must be alphanumeric, _, or -', + }); + } +}; + const createUniqueIdValidation = (ids: Set) => { - const uniqueIdCheck: ValidationFunc = ({ value }) => { + const uniqueIdCheck = (value: string) => { if (ids.has(value)) { - return { - message: i18n.translate('xpack.osquery.pack.queryFlyoutForm.uniqueIdError', { - defaultMessage: 'ID must be unique', - }), - }; + return i18n.translate('xpack.osquery.pack.queryFlyoutForm.uniqueIdError', { + defaultMessage: 'ID must be unique', + }); } }; return uniqueIdCheck; }; -export const createIdFieldValidations = (ids: Set) => [ - fieldValidators.emptyField( - i18n.translate('xpack.osquery.pack.queryFlyoutForm.emptyIdError', { +export const createFormIdFieldValidations = (ids: Set) => ({ + required: { + message: i18n.translate('xpack.osquery.pack.queryFlyoutForm.emptyIdError', { defaultMessage: 'ID is required', - }) - ), - idSchemaValidation, - createUniqueIdValidation(ids), -]; - -export const intervalFieldValidations: Array> = [ - { - validator: fieldValidators.numberGreaterThanField({ - than: 0, - message: i18n.translate('xpack.osquery.pack.queryFlyoutForm.intervalFieldMinNumberError', { - defaultMessage: 'A positive interval value is required', - }), }), + value: true, }, - { - validator: fieldValidators.numberSmallerThanField({ - than: 604800, - message: ({ than }) => - i18n.translate('xpack.osquery.pack.queryFlyoutForm.intervalFieldMaxNumberError', { - defaultMessage: 'An interval value must be lower than {than}', - values: { than }, - }), - }), + validate: (text: string) => { + const isPatternValid = idHookSchemaValidation(text); + const isUnique = createUniqueIdValidation(ids)(text); + + return isPatternValid || isUnique; }, -]; +}); diff --git a/x-pack/plugins/osquery/public/packs/types.ts b/x-pack/plugins/osquery/public/packs/types.ts index 4a4253b35b048..dcdb03045ab22 100644 --- a/x-pack/plugins/osquery/public/packs/types.ts +++ b/x-pack/plugins/osquery/public/packs/types.ts @@ -6,24 +6,6 @@ */ import type { SavedObject } from '@kbn/core/public'; import type { PackQueryFormData } from './queries/use_pack_query_form'; -import type { PackQueryECSMapping } from './queries/use_pack_query_form'; - -export interface IQueryPayload { - attributes?: { - name: string; - id: string; - }; -} - -export interface PackItemQuery { - id: string; - name: string; - interval: number; - query: string; - platform?: string; - version?: string; - ecs_mapping?: PackQueryECSMapping[]; -} export type PackSavedObject = SavedObject<{ name: string; diff --git a/x-pack/plugins/osquery/public/packs/use_create_pack.ts b/x-pack/plugins/osquery/public/packs/use_create_pack.ts index b536f50075a86..26cf1fad2eeb9 100644 --- a/x-pack/plugins/osquery/public/packs/use_create_pack.ts +++ b/x-pack/plugins/osquery/public/packs/use_create_pack.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useMutation, useQueryClient } from 'react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; import { useKibana } from '../common/lib/kibana'; @@ -42,7 +42,7 @@ export const useCreatePack = ({ withRedirect }: UseCreatePackProps) => { setErrorToast(error, { title: error.body.error, toastMessage: error.body.message }); }, onSuccess: (payload) => { - queryClient.invalidateQueries(PACKS_ID); + queryClient.invalidateQueries([PACKS_ID]); if (withRedirect) { navigateToApp(PLUGIN_ID, { path: pagePathGetters.packs() }); } diff --git a/x-pack/plugins/osquery/public/packs/use_delete_pack.ts b/x-pack/plugins/osquery/public/packs/use_delete_pack.ts index 439a850c785b0..8b5e7f0fb1450 100644 --- a/x-pack/plugins/osquery/public/packs/use_delete_pack.ts +++ b/x-pack/plugins/osquery/public/packs/use_delete_pack.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useMutation, useQueryClient } from 'react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; import { useKibana } from '../common/lib/kibana'; @@ -36,7 +36,7 @@ export const useDeletePack = ({ packId, withRedirect }: UseDeletePackProps) => { }); }, onSuccess: () => { - queryClient.invalidateQueries(PACKS_ID); + queryClient.invalidateQueries([PACKS_ID]); if (withRedirect) { navigateToApp(PLUGIN_ID, { path: pagePathGetters.packs() }); } diff --git a/x-pack/plugins/osquery/public/packs/use_pack.ts b/x-pack/plugins/osquery/public/packs/use_pack.ts index da6062fb8309f..bedaceff9c63c 100644 --- a/x-pack/plugins/osquery/public/packs/use_pack.ts +++ b/x-pack/plugins/osquery/public/packs/use_pack.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { useKibana } from '../common/lib/kibana'; import type { PackItem } from './types'; diff --git a/x-pack/plugins/osquery/public/packs/use_pack_query_errors.ts b/x-pack/plugins/osquery/public/packs/use_pack_query_errors.ts index 1e57af58c46b4..95b2ddc7c9268 100644 --- a/x-pack/plugins/osquery/public/packs/use_pack_query_errors.ts +++ b/x-pack/plugins/osquery/public/packs/use_pack_query_errors.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { lastValueFrom } from 'rxjs'; import type { DataView } from '@kbn/data-plugin/common'; import { SortDirection } from '@kbn/data-plugin/common'; diff --git a/x-pack/plugins/osquery/public/packs/use_pack_query_last_results.ts b/x-pack/plugins/osquery/public/packs/use_pack_query_last_results.ts index 28b474530f4bc..15fbcce34a862 100644 --- a/x-pack/plugins/osquery/public/packs/use_pack_query_last_results.ts +++ b/x-pack/plugins/osquery/public/packs/use_pack_query_last_results.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import moment from 'moment-timezone'; import { lastValueFrom } from 'rxjs'; import { SortDirection } from '@kbn/data-plugin/common'; diff --git a/x-pack/plugins/osquery/public/packs/use_packs.ts b/x-pack/plugins/osquery/public/packs/use_packs.ts index b8fadaf571a84..05dd200b8f1c8 100644 --- a/x-pack/plugins/osquery/public/packs/use_packs.ts +++ b/x-pack/plugins/osquery/public/packs/use_packs.ts @@ -6,7 +6,7 @@ */ import type { SavedObjectsFindResponse } from '@kbn/core/public'; -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { useKibana } from '../common/lib/kibana'; import { PACKS_ID } from './constants'; diff --git a/x-pack/plugins/osquery/public/packs/use_update_pack.ts b/x-pack/plugins/osquery/public/packs/use_update_pack.ts index 1075078dd0e98..840d01e9b32fd 100644 --- a/x-pack/plugins/osquery/public/packs/use_update_pack.ts +++ b/x-pack/plugins/osquery/public/packs/use_update_pack.ts @@ -5,8 +5,8 @@ * 2.0. */ -import type { UseMutationOptions } from 'react-query'; -import { useMutation, useQueryClient } from 'react-query'; +import type { UseMutationOptions } from '@tanstack/react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; import { useKibana } from '../common/lib/kibana'; @@ -48,7 +48,7 @@ export const useUpdatePack = ({ withRedirect, options }: UseUpdatePackProps) => setErrorToast(error, { title: error?.body?.error, toastMessage: error?.body?.message }); }, onSuccess: (response) => { - queryClient.invalidateQueries(PACKS_ID); + queryClient.invalidateQueries([PACKS_ID]); if (withRedirect) { navigateToApp(PLUGIN_ID, { path: pagePathGetters.packs() }); } diff --git a/x-pack/plugins/osquery/public/query_client.ts b/x-pack/plugins/osquery/public/query_client.ts index a0ee6c71be288..41467404c7bff 100644 --- a/x-pack/plugins/osquery/public/query_client.ts +++ b/x-pack/plugins/osquery/public/query_client.ts @@ -5,14 +5,12 @@ * 2.0. */ -/* eslint-disable @typescript-eslint/no-empty-function */ +import { QueryClient } from '@tanstack/react-query'; -import { QueryClient, setLogger } from 'react-query'; - -setLogger({ - log: () => {}, - warn: () => {}, - error: () => {}, +export const queryClient = new QueryClient({ + logger: { + log: () => null, + warn: () => null, + error: () => null, + }, }); - -export const queryClient = new QueryClient(); diff --git a/x-pack/plugins/osquery/public/results/results_table.tsx b/x-pack/plugins/osquery/public/results/results_table.tsx index a00818ae4857d..a1658538045a6 100644 --- a/x-pack/plugins/osquery/public/results/results_table.tsx +++ b/x-pack/plugins/osquery/public/results/results_table.tsx @@ -28,6 +28,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import React, { createContext, useEffect, useState, useCallback, useContext, useMemo } from 'react'; import { pagePathGetters } from '@kbn/fleet-plugin/public'; +import type { ECSMapping } from '../../common/schemas/common'; import { useAllResults } from './use_all_results'; import type { ResultEdges } from '../../common/search_strategy'; import { Direction } from '../../common/search_strategy'; @@ -48,7 +49,7 @@ interface ResultsTableComponentProps { actionId: string; selectedAgent?: string; agentIds?: string[]; - ecsMapping?: Record; + ecsMapping?: ECSMapping; endDate?: string; startDate?: string; addToTimeline?: (payload: { query: [string, string]; isIcon?: true }) => React.ReactElement; @@ -186,10 +187,8 @@ const ResultsTableComponent: React.FC = ({ if (!ecsMapping) return; return reduce( - (acc, [key, value]) => { - // @ts-expect-error update types + (acc: Record, [key, value]) => { if (value?.field) { - // @ts-expect-error update types acc[value?.field] = [...(acc[value?.field] ?? []), key]; } @@ -202,7 +201,6 @@ const ResultsTableComponent: React.FC = ({ const getHeaderDisplay = useCallback( (columnName: string) => { - // @ts-expect-error update types if (ecsMappingConfig && ecsMappingConfig[columnName]) { return ( <> @@ -217,12 +215,9 @@ const ResultsTableComponent: React.FC = ({ /> {`:`}
    - { - // @ts-expect-error update types - ecsMappingConfig[columnName].map((fieldName) => ( -
  • {fieldName}
  • - )) - } + {ecsMappingConfig[columnName].map((fieldName) => ( +
  • {fieldName}
  • + ))}
} diff --git a/x-pack/plugins/osquery/public/results/use_all_results.ts b/x-pack/plugins/osquery/public/results/use_all_results.ts index 94a80fb09734a..ee545a2eaf9e9 100644 --- a/x-pack/plugins/osquery/public/results/use_all_results.ts +++ b/x-pack/plugins/osquery/public/results/use_all_results.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; import { lastValueFrom } from 'rxjs'; diff --git a/x-pack/plugins/osquery/public/routes/saved_queries/edit/form.tsx b/x-pack/plugins/osquery/public/routes/saved_queries/edit/form.tsx index f5ab2308d1720..1b24b4a71eeb5 100644 --- a/x-pack/plugins/osquery/public/routes/saved_queries/edit/form.tsx +++ b/x-pack/plugins/osquery/public/routes/saved_queries/edit/form.tsx @@ -16,14 +16,17 @@ import { import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { PackQueryFormData } from '../../../packs/queries/use_pack_query_form'; +import { FormProvider } from 'react-hook-form'; import { useRouterNavigate } from '../../../common/lib/kibana'; -import { Form } from '../../../shared_imports'; import { SavedQueryForm } from '../../../saved_queries/form'; +import type { + SavedQueryFormData, + SavedQuerySOFormData, +} from '../../../saved_queries/form/use_saved_query_form'; import { useSavedQueryForm } from '../../../saved_queries/form/use_saved_query_form'; interface EditSavedQueryFormProps { - defaultValue?: PackQueryFormData; + defaultValue?: SavedQuerySOFormData; handleSubmit: (payload: unknown) => Promise; viewMode?: boolean; } @@ -35,15 +38,28 @@ const EditSavedQueryFormComponent: React.FC = ({ }) => { const savedQueryListProps = useRouterNavigate('saved_queries'); - const { form } = useSavedQueryForm({ + const hooksForm = useSavedQueryForm({ defaultValue, - handleSubmit, }); - const { submit, isSubmitting } = form; + + const { + serializer, + idSet, + handleSubmit: formSubmit, + formState: { isSubmitting }, + } = hooksForm; + + const onSubmit = (payload: SavedQueryFormData) => { + const serializedData = serializer(payload); + try { + handleSubmit(serializedData); + // eslint-disable-next-line no-empty + } catch (e) {} + }; return ( -
- + + {!viewMode && ( <> @@ -65,7 +81,7 @@ const EditSavedQueryFormComponent: React.FC = ({ fill size="m" iconType="save" - onClick={submit} + onClick={formSubmit(onSubmit)} > = ({ )} - + ); }; diff --git a/x-pack/plugins/osquery/public/routes/saved_queries/edit/tabs.tsx b/x-pack/plugins/osquery/public/routes/saved_queries/edit/tabs.tsx index c8e57f4f5db23..081530479c8b1 100644 --- a/x-pack/plugins/osquery/public/routes/saved_queries/edit/tabs.tsx +++ b/x-pack/plugins/osquery/public/routes/saved_queries/edit/tabs.tsx @@ -9,6 +9,7 @@ import { EuiTabbedContent, EuiNotificationBadge } from '@elastic/eui'; import React, { useMemo } from 'react'; import type { ReactElement } from 'react'; +import type { ECSMapping } from '../../../../common/schemas/common'; import { ResultsTable } from '../../../results/results_table'; import { ActionResultsSummary } from '../../../action_results/action_results_summary'; @@ -16,7 +17,7 @@ interface ResultTabsProps { actionId: string; agentIds?: string[]; startDate?: string; - ecsMapping?: Record; + ecsMapping?: ECSMapping; failedAgentsCount?: number; endDate?: string; addToTimeline?: (payload: { query: [string, string]; isIcon?: true }) => ReactElement; diff --git a/x-pack/plugins/osquery/public/routes/saved_queries/new/form.tsx b/x-pack/plugins/osquery/public/routes/saved_queries/new/form.tsx index 4dc26c5aaedf4..350c35b2b3fa5 100644 --- a/x-pack/plugins/osquery/public/routes/saved_queries/new/form.tsx +++ b/x-pack/plugins/osquery/public/routes/saved_queries/new/form.tsx @@ -15,15 +15,19 @@ import { } from '@elastic/eui'; import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; +import { FormProvider } from 'react-hook-form'; -import type { PackQueryFormData } from '../../../packs/queries/use_pack_query_form'; +import { isEmpty } from 'lodash'; import { useRouterNavigate } from '../../../common/lib/kibana'; -import { Form } from '../../../shared_imports'; import { SavedQueryForm } from '../../../saved_queries/form'; +import type { + SavedQuerySOFormData, + SavedQueryFormData, +} from '../../../saved_queries/form/use_saved_query_form'; import { useSavedQueryForm } from '../../../saved_queries/form/use_saved_query_form'; interface NewSavedQueryFormProps { - defaultValue?: PackQueryFormData; + defaultValue?: SavedQuerySOFormData; handleSubmit: (payload: unknown) => Promise; } @@ -33,15 +37,24 @@ const NewSavedQueryFormComponent: React.FC = ({ }) => { const savedQueryListProps = useRouterNavigate('saved_queries'); - const { form } = useSavedQueryForm({ + const hooksForm = useSavedQueryForm({ defaultValue, - handleSubmit, }); - const { submit, isSubmitting, isValid } = form; + const { + serializer, + idSet, + handleSubmit: formSubmit, + formState: { isSubmitting, errors }, + } = hooksForm; + + const onSubmit = (payload: SavedQueryFormData) => { + const serializedData = serializer(payload); + handleSubmit(serializedData); + }; return ( -
- + + @@ -61,7 +74,7 @@ const NewSavedQueryFormComponent: React.FC = ({ fill size="m" iconType="save" - onClick={submit} + onClick={formSubmit(onSubmit)} > = ({ - + ); }; diff --git a/x-pack/plugins/osquery/public/saved_queries/form/code_editor_field.tsx b/x-pack/plugins/osquery/public/saved_queries/form/code_editor_field.tsx index de9d62fe1406c..2f4ee427461dc 100644 --- a/x-pack/plugins/osquery/public/saved_queries/form/code_editor_field.tsx +++ b/x-pack/plugins/osquery/public/saved_queries/form/code_editor_field.tsx @@ -10,9 +10,11 @@ import { EuiCodeBlock, EuiFormRow } from '@elastic/eui'; import React from 'react'; import styled from 'styled-components'; +import { useController } from 'react-hook-form'; +import { i18n } from '@kbn/i18n'; +import { MAX_QUERY_LENGTH } from '../../packs/queries/validations'; import { OsquerySchemaLink } from '../../components/osquery_schema_link'; import { OsqueryEditor } from '../../editor'; -import type { FieldHook } from '../../shared_imports'; const StyledEuiCodeBlock = styled(EuiCodeBlock)` min-height: 100px; @@ -20,20 +22,47 @@ const StyledEuiCodeBlock = styled(EuiCodeBlock)` interface CodeEditorFieldProps { euiFieldProps?: Record; - field: FieldHook; + labelAppend?: string; + helpText?: string; } -const CodeEditorFieldComponent: React.FC = ({ euiFieldProps, field }) => { - const { value, label, labelAppend, helpText, setValue, errors } = field; - const error = errors[0]?.message; +const CodeEditorFieldComponent: React.FC = ({ + euiFieldProps, + labelAppend, + helpText, +}) => { + const { + field: { onChange, value }, + fieldState: { error }, + } = useController({ + name: 'query', + rules: { + required: { + message: i18n.translate('xpack.osquery.pack.queryFlyoutForm.emptyQueryError', { + defaultMessage: 'Query is a required field', + }), + value: true, + }, + maxLength: { + message: i18n.translate('xpack.osquery.liveQuery.queryForm.largeQueryError', { + defaultMessage: 'Query is too large (max {maxLength} characters)', + values: { maxLength: MAX_QUERY_LENGTH }, + }), + value: MAX_QUERY_LENGTH, + }, + }, + defaultValue: '', + }); return ( } helpText={helpText} - isInvalid={typeof error === 'string'} - error={error} + isInvalid={!!error?.message} + error={error?.message} fullWidth > {euiFieldProps?.isDisabled ? ( @@ -46,7 +75,7 @@ const CodeEditorFieldComponent: React.FC = ({ euiFieldProp {value} ) : ( - + )} ); diff --git a/x-pack/plugins/osquery/public/saved_queries/form/index.tsx b/x-pack/plugins/osquery/public/saved_queries/form/index.tsx index ec87f430eea92..6bddd7f0508e4 100644 --- a/x-pack/plugins/osquery/public/saved_queries/form/index.tsx +++ b/x-pack/plugins/osquery/public/saved_queries/form/index.tsx @@ -17,25 +17,25 @@ import React, { useCallback, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { ALL_OSQUERY_VERSIONS_OPTIONS } from '../../packs/queries/constants'; +import { IntervalField, QueryIdField, QueryDescriptionField, VersionField } from '../../form'; import { PlatformCheckBoxGroupField } from '../../packs/queries/platform_checkbox_group_field'; -import { Field, getUseField, UseField } from '../../shared_imports'; -import { CodeEditorField } from './code_editor_field'; +import { ALL_OSQUERY_VERSIONS_OPTIONS } from '../../packs/queries/constants'; import { ECSMappingEditorField } from '../../packs/queries/lazy_ecs_mapping_editor_field'; import { PlaygroundFlyout } from './playground_flyout'; - -export const CommonUseField = getUseField({ component: Field }); +import { CodeEditorField } from './code_editor_field'; interface SavedQueryFormProps { viewMode?: boolean; hasPlayground?: boolean; isValid?: boolean; + idSet?: Set; } const SavedQueryFormComponent: React.FC = ({ viewMode, hasPlayground, isValid, + idSet, }) => { const [playgroundVisible, setPlaygroundVisible] = useState(false); @@ -77,11 +77,11 @@ const SavedQueryFormComponent: React.FC = ({ return ( <> - + - + - + @@ -119,16 +119,12 @@ const SavedQueryFormComponent: React.FC = ({ - + - + - + {playgroundVisible && ( diff --git a/x-pack/plugins/osquery/public/saved_queries/form/playground_flyout.tsx b/x-pack/plugins/osquery/public/saved_queries/form/playground_flyout.tsx index a253fada318a0..741651b5f2a9a 100644 --- a/x-pack/plugins/osquery/public/saved_queries/form/playground_flyout.tsx +++ b/x-pack/plugins/osquery/public/saved_queries/form/playground_flyout.tsx @@ -10,8 +10,8 @@ import React, { useMemo } from 'react'; import styled from 'styled-components'; import { FormattedMessage } from '@kbn/i18n-react'; +import { useFormContext } from 'react-hook-form'; import { LiveQuery } from '../../live_queries'; -import { useFormData } from '../../shared_imports'; const StyledEuiFlyoutHeader = styled(EuiFlyoutHeader)` &.euiFlyoutHeader.euiFlyoutHeader--hasBorder { @@ -26,11 +26,13 @@ interface PlaygroundFlyoutProps { } const PlaygroundFlyoutComponent: React.FC = ({ enabled, onClose }) => { - const [{ query, ecs_mapping: ecsMapping, id }, formDataSerializer] = useFormData(); - + // @ts-expect-error update types + const { serializer, watch } = useFormContext(); + const watchedValues = watch(); + const { query, ecs_mapping: ecsMapping, id } = watchedValues; /* recalculate the form data when ecs_mapping changes */ // eslint-disable-next-line react-hooks/exhaustive-deps - const serializedFormData = useMemo(() => formDataSerializer(), [ecsMapping, formDataSerializer]); + const serializedFormData = useMemo(() => serializer(watchedValues), [ecsMapping]); return ( diff --git a/x-pack/plugins/osquery/public/saved_queries/form/use_saved_query_form.tsx b/x-pack/plugins/osquery/public/saved_queries/form/use_saved_query_form.tsx index 92849b57f7122..36f303c8dfff3 100644 --- a/x-pack/plugins/osquery/public/saved_queries/form/use_saved_query_form.tsx +++ b/x-pack/plugins/osquery/public/saved_queries/form/use_saved_query_form.tsx @@ -5,105 +5,98 @@ * 2.0. */ +import { useForm as useHookForm } from 'react-hook-form'; import { isArray, isEmpty, map } from 'lodash'; -import uuid from 'uuid'; -import { produce } from 'immer'; +import type { Draft } from 'immer'; +import produce from 'immer'; import { useMemo } from 'react'; - +import type { ECSMapping } from '../../../common/schemas/common'; import { convertECSMappingToObject } from '../../../common/schemas/common/utils'; -import { useForm } from '../../shared_imports'; -import { createFormSchema } from '../../packs/queries/schema'; -import type { - PackQueryECSMapping, - PackQueryFormData, -} from '../../packs/queries/use_pack_query_form'; +import type { EcsMappingFormField } from '../../packs/queries/ecs_mapping_editor_field'; +import { defaultEcsFormData } from '../../packs/queries/ecs_mapping_editor_field'; import { useSavedQueries } from '../use_saved_queries'; -const SAVED_QUERY_FORM_ID = 'savedQueryForm'; +export interface SavedQuerySOFormData { + id?: string; + description?: string; + query?: string; + interval?: string; + platform?: string; + version?: string | undefined; + ecs_mapping?: ECSMapping | undefined; +} -interface ReturnFormData { +export interface SavedQueryFormData { id?: string; description?: string; - query: string; + query?: string; interval?: number; platform?: string; version?: string[]; - ecs_mapping?: PackQueryECSMapping[] | undefined; + ecs_mapping: EcsMappingFormField[]; } interface UseSavedQueryFormProps { - defaultValue?: PackQueryFormData; - handleSubmit: (payload: unknown) => Promise; + defaultValue?: SavedQuerySOFormData; } -export const useSavedQueryForm = ({ defaultValue, handleSubmit }: UseSavedQueryFormProps) => { +const deserializer = (payload: SavedQuerySOFormData): SavedQueryFormData => ({ + id: payload.id, + description: payload.description, + query: payload.query, + interval: payload.interval ? parseInt(payload.interval, 10) : 3600, + platform: payload.platform, + version: payload.version ? [payload.version] : [], + ecs_mapping: !isEmpty(payload.ecs_mapping) + ? (map(payload.ecs_mapping, (value, key: string) => ({ + key, + result: { + type: Object.keys(value)[0], + value: Object.values(value)[0], + }, + })) as unknown as EcsMappingFormField[]) + : [defaultEcsFormData], +}); + +export const savedQueryDataSerializer = (payload: SavedQueryFormData): SavedQuerySOFormData => + // @ts-expect-error update types + produce(payload, (draft: Draft) => { + if (isArray(draft.version)) { + if (!draft.version.length) { + draft.version = ''; + } else { + draft.version = draft.version[0]; + } + } + + if (isArray(draft.platform) && !draft.platform.length) { + delete draft.platform; + } + + draft.ecs_mapping = convertECSMappingToObject(payload.ecs_mapping); + + if (draft.interval) { + draft.interval = draft.interval + ''; + } + + return draft; + }); + +export const useSavedQueryForm = ({ defaultValue }: UseSavedQueryFormProps) => { const { data } = useSavedQueries({}); - const ids: string[] = useMemo(() => map(data, 'attributes.id') ?? [], [data]); + const ids: string[] = useMemo(() => map(data?.data, 'attributes.id') ?? [], [data]); const idSet = useMemo>(() => { const res = new Set(ids); if (defaultValue && defaultValue.id) res.delete(defaultValue.id); return res; }, [ids, defaultValue]); - const formSchema = useMemo(() => createFormSchema(idSet), [idSet]); - - return useForm({ - id: SAVED_QUERY_FORM_ID + uuid.v4(), - schema: formSchema, - onSubmit: async (formData, isValid) => { - if (isValid) { - try { - await handleSubmit(formData); - // eslint-disable-next-line no-empty - } catch (e) {} - } - }, - defaultValue, - // @ts-expect-error update types - serializer: (payload) => - produce(payload, (draft) => { - if (isArray(draft.version)) { - if (!draft.version.length) { - // @ts-expect-error update types - draft.version = ''; - } else { - // @ts-expect-error update types - draft.version = draft.version[0]; - } - } - if (isEmpty(payload.ecs_mapping)) { - delete draft.ecs_mapping; - } else { - // @ts-expect-error update types - draft.ecs_mapping = convertECSMappingToObject(payload.ecs_mapping); - } - - // @ts-expect-error update types - draft.interval = draft.interval + ''; - - return draft; - }), - deserializer: (payload) => { - if (!payload) return {} as ReturnFormData; - - return { - id: payload.id, - description: payload.description, - query: payload.query, - interval: payload.interval ?? 3600, - platform: payload.platform, - version: payload.version ? [payload.version] : [], - ecs_mapping: (!isEmpty(payload.ecs_mapping) - ? map(payload.ecs_mapping, (value, key: string) => ({ - key, - result: { - type: Object.keys(value)[0], - value: Object.values(value)[0], - }, - })) - : ([] as PackQueryECSMapping[])) as PackQueryECSMapping[], - }; - }, - }); + return { + serializer: savedQueryDataSerializer, + idSet, + ...useHookForm({ + defaultValues: defaultValue ? deserializer(defaultValue) : {}, + }), + }; }; diff --git a/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx b/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx index c70bad1a617c1..eedaf8ac5ce0f 100644 --- a/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx +++ b/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx @@ -9,11 +9,11 @@ import { find } from 'lodash/fp'; import { EuiCodeBlock, EuiFormRow, EuiComboBox, EuiTextColor } from '@elastic/eui'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; +import { useWatch } from 'react-hook-form'; import { QUERIES_DROPDOWN_LABEL, QUERIES_DROPDOWN_SEARCH_FIELD_LABEL } from './constants'; import { OsquerySchemaLink } from '../components/osquery_schema_link'; import { useSavedQueries } from './use_saved_queries'; -import { useFormData } from '../shared_imports'; import type { SavedQuerySO } from '../routes/saved_queries/list'; const TextTruncate = styled.div` @@ -49,10 +49,9 @@ const SavedQueriesDropdownComponent: React.FC = ({ disabled, onChange, }) => { + const savedQueryId = useWatch({ name: 'savedQueryId' }); const [selectedOptions, setSelectedOptions] = useState([]); - const [{ savedQueryId }] = useFormData(); - const { data } = useSavedQueries({}); const queryOptions = useMemo( diff --git a/x-pack/plugins/osquery/public/saved_queries/saved_query_flyout.tsx b/x-pack/plugins/osquery/public/saved_queries/saved_query_flyout.tsx index 1e92294a5f1b4..abf8138ff11fa 100644 --- a/x-pack/plugins/osquery/public/saved_queries/saved_query_flyout.tsx +++ b/x-pack/plugins/osquery/public/saved_queries/saved_query_flyout.tsx @@ -17,17 +17,18 @@ import { EuiButtonEmpty, EuiButton, } from '@elastic/eui'; +import { FormProvider } from 'react-hook-form'; + import React, { useCallback } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { Form } from '../shared_imports'; +import type { SavedQuerySOFormData, SavedQueryFormData } from './form/use_saved_query_form'; import { useSavedQueryForm } from './form/use_saved_query_form'; import { SavedQueryForm } from './form'; import { useCreateSavedQuery } from './use_create_saved_query'; -import type { PackQueryFormData } from '../packs/queries/use_pack_query_form'; interface AddQueryFlyoutProps { - defaultValue: PackQueryFormData; + defaultValue: SavedQuerySOFormData; onClose: () => void; isExternal?: boolean; } @@ -41,18 +42,24 @@ const SavedQueryFlyoutComponent: React.FC = ({ }) => { const createSavedQueryMutation = useCreateSavedQuery({ withRedirect: false }); - const handleSubmit = useCallback( - async (payload) => { - await createSavedQueryMutation.mutateAsync(payload).then(() => onClose()); - }, - [createSavedQueryMutation, onClose] - ); - - const { form } = useSavedQueryForm({ + const hooksForm = useSavedQueryForm({ defaultValue, - handleSubmit, }); - const { submit, isSubmitting } = form; + const { + serializer, + idSet, + handleSubmit, + formState: { isSubmitting }, + } = hooksForm; + const onSubmit = useCallback( + async (payload: SavedQueryFormData) => { + const serializedData = serializer(payload); + // TODO CHECK THIS + // @ts-expect-error update types + await createSavedQueryMutation.mutateAsync(serializedData).then(() => onClose()); + }, + [createSavedQueryMutation, onClose, serializer] + ); return ( @@ -74,9 +81,9 @@ const SavedQueryFlyoutComponent: React.FC = ({ -
- - + + +
@@ -89,7 +96,7 @@ const SavedQueryFlyoutComponent: React.FC = ({
- + { - queryClient.invalidateQueries(SAVED_QUERIES_ID); + queryClient.invalidateQueries([SAVED_QUERIES_ID]); if (withRedirect) { navigateToApp(PLUGIN_ID, { path: pagePathGetters.saved_queries() }); } diff --git a/x-pack/plugins/osquery/public/saved_queries/use_delete_saved_query.ts b/x-pack/plugins/osquery/public/saved_queries/use_delete_saved_query.ts index e882a9ba2f1ea..f03ac709db1ee 100644 --- a/x-pack/plugins/osquery/public/saved_queries/use_delete_saved_query.ts +++ b/x-pack/plugins/osquery/public/saved_queries/use_delete_saved_query.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useMutation, useQueryClient } from 'react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; import { useKibana } from '../common/lib/kibana'; @@ -35,7 +35,7 @@ export const useDeleteSavedQuery = ({ savedQueryId }: UseDeleteSavedQueryProps) }); }, onSuccess: () => { - queryClient.invalidateQueries(SAVED_QUERIES_ID); + queryClient.invalidateQueries([SAVED_QUERIES_ID]); navigateToApp(PLUGIN_ID, { path: pagePathGetters.saved_queries() }); toasts.addSuccess( i18n.translate('xpack.osquery.editSavedQuery.deleteSuccessToastMessageText', { diff --git a/x-pack/plugins/osquery/public/saved_queries/use_saved_queries.ts b/x-pack/plugins/osquery/public/saved_queries/use_saved_queries.ts index 53d07c52e325b..bceeaed5be435 100644 --- a/x-pack/plugins/osquery/public/saved_queries/use_saved_queries.ts +++ b/x-pack/plugins/osquery/public/saved_queries/use_saved_queries.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import type { SavedObjectsFindResponse } from '@kbn/core/public'; import { useKibana } from '../common/lib/kibana'; diff --git a/x-pack/plugins/osquery/public/saved_queries/use_saved_query.ts b/x-pack/plugins/osquery/public/saved_queries/use_saved_query.ts index f146910f35c14..e5c39d76c94f2 100644 --- a/x-pack/plugins/osquery/public/saved_queries/use_saved_query.ts +++ b/x-pack/plugins/osquery/public/saved_queries/use_saved_query.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { PLUGIN_ID } from '../../common'; import { useKibana } from '../common/lib/kibana'; diff --git a/x-pack/plugins/osquery/public/saved_queries/use_update_saved_query.ts b/x-pack/plugins/osquery/public/saved_queries/use_update_saved_query.ts index 95eb446e147bd..aef48b7577ece 100644 --- a/x-pack/plugins/osquery/public/saved_queries/use_update_saved_query.ts +++ b/x-pack/plugins/osquery/public/saved_queries/use_update_saved_query.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useMutation, useQueryClient } from 'react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; import { useKibana } from '../common/lib/kibana'; @@ -40,15 +40,15 @@ export const useUpdateSavedQuery = ({ savedQueryId }: UseUpdateSavedQueryProps) toastMessage: error.body.message, }); }, - onSuccess: (payload: SavedQuerySO) => { - queryClient.invalidateQueries(SAVED_QUERIES_ID); + onSuccess: (payload: { data: SavedQuerySO }) => { + queryClient.invalidateQueries([SAVED_QUERIES_ID]); queryClient.invalidateQueries([SAVED_QUERY_ID, { savedQueryId }]); navigateToApp(PLUGIN_ID, { path: pagePathGetters.saved_queries() }); toasts.addSuccess( i18n.translate('xpack.osquery.editSavedQuery.successToastMessageText', { defaultMessage: 'Successfully updated "{savedQueryName}" query', values: { - savedQueryName: payload.attributes?.id ?? '', + savedQueryName: payload.data.attributes?.id ?? '', }, }) ); diff --git a/x-pack/plugins/osquery/public/shared_components/osquery_action/index.tsx b/x-pack/plugins/osquery/public/shared_components/osquery_action/index.tsx index 3cffdbde50dd1..15c6fa645de11 100644 --- a/x-pack/plugins/osquery/public/shared_components/osquery_action/index.tsx +++ b/x-pack/plugins/osquery/public/shared_components/osquery_action/index.tsx @@ -7,7 +7,7 @@ import { EuiErrorBoundary, EuiLoadingContent, EuiEmptyPrompt, EuiCode } from '@elastic/eui'; import React, { useMemo } from 'react'; -import { QueryClientProvider } from 'react-query'; +import { QueryClientProvider } from '@tanstack/react-query'; import type { CoreStart } from '@kbn/core/public'; import { AGENT_STATUS_ERROR, diff --git a/x-pack/plugins/osquery/public/shared_components/osquery_action/osquery_action.test.tsx b/x-pack/plugins/osquery/public/shared_components/osquery_action/osquery_action.test.tsx index 4c9214ca3ea14..927d408884d20 100644 --- a/x-pack/plugins/osquery/public/shared_components/osquery_action/osquery_action.test.tsx +++ b/x-pack/plugins/osquery/public/shared_components/osquery_action/osquery_action.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { render } from '@testing-library/react'; -import { QueryClientProvider } from 'react-query'; +import { QueryClientProvider } from '@tanstack/react-query'; import { OsqueryAction } from '.'; import { queryClient } from '../../query_client'; diff --git a/x-pack/plugins/security/public/authentication/access_agreement/__snapshots__/access_agreement_page.test.tsx.snap b/x-pack/plugins/security/public/authentication/access_agreement/__snapshots__/access_agreement_page.test.tsx.snap index 04dd39c69e64d..2735c8d14d1a1 100644 --- a/x-pack/plugins/security/public/authentication/access_agreement/__snapshots__/access_agreement_page.test.tsx.snap +++ b/x-pack/plugins/security/public/authentication/access_agreement/__snapshots__/access_agreement_page.test.tsx.snap @@ -2,42 +2,18 @@ exports[`AccessAgreementPage renders as expected when state is available 1`] = ` - -

- - This is - - - - link - - -

-
+ link + +

`; diff --git a/x-pack/plugins/security/public/authentication/login/components/login_form/__snapshots__/login_form.test.tsx.snap b/x-pack/plugins/security/public/authentication/login/components/login_form/__snapshots__/login_form.test.tsx.snap index d6eb4c20b8003..9e150c2ff0312 100644 --- a/x-pack/plugins/security/public/authentication/login/components/login_form/__snapshots__/login_form.test.tsx.snap +++ b/x-pack/plugins/security/public/authentication/login/components/login_form/__snapshots__/login_form.test.tsx.snap @@ -2,103 +2,49 @@ exports[`LoginForm login selector properly switches to login form -> login help and back: Login Help 1`] = ` - -

- - - some help - - -

-
+ some help + +

`; exports[`LoginForm login selector properly switches to login help: Login Help 1`] = ` - -

- - - some help - - -

-
+ some help + +

`; exports[`LoginForm properly switches to login help: Login Help 1`] = ` - -

- - - some help - - -

-
+ some help + +

`; diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.test.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.test.tsx index 514e9aa5e306c..c4f3000277af7 100644 --- a/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.test.tsx +++ b/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.test.tsx @@ -89,7 +89,9 @@ describe('apiKeysManagementApp', () => {
`); - unmount!(); + act(() => { + unmount!(); + }); expect(docTitle.reset).toHaveBeenCalledTimes(1); expect(container).toMatchInlineSnapshot(`
`); diff --git a/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.test.tsx b/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.test.tsx index 04db6c58c27d1..c085bafb562c2 100644 --- a/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.test.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.test.tsx @@ -82,7 +82,9 @@ describe('roleMappingsManagementApp', () => {
`); - unmount(); + act(() => { + unmount(); + }); expect(docTitle.reset).toHaveBeenCalledTimes(1); @@ -105,7 +107,9 @@ describe('roleMappingsManagementApp', () => {
`); - unmount(); + act(() => { + unmount(); + }); expect(docTitle.reset).toHaveBeenCalledTimes(1); @@ -133,7 +137,9 @@ describe('roleMappingsManagementApp', () => {
`); - unmount(); + act(() => { + unmount(); + }); expect(docTitle.reset).toHaveBeenCalledTimes(1); diff --git a/x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx b/x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx index 327499c64bb76..a6e06351f38c9 100644 --- a/x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx +++ b/x-pack/plugins/security/public/management/roles/roles_management_app.test.tsx @@ -88,7 +88,9 @@ describe('rolesManagementApp', () => { `); - unmount(); + act(() => { + unmount(); + }); expect(docTitle.reset).toHaveBeenCalledTimes(1); @@ -108,7 +110,9 @@ describe('rolesManagementApp', () => { `); - unmount(); + act(() => { + unmount(); + }); expect(docTitle.reset).toHaveBeenCalledTimes(1); @@ -133,7 +137,9 @@ describe('rolesManagementApp', () => { `); - unmount(); + act(() => { + unmount(); + }); expect(docTitle.reset).toHaveBeenCalledTimes(1); @@ -158,7 +164,9 @@ describe('rolesManagementApp', () => { `); - unmount(); + act(() => { + unmount(); + }); expect(docTitle.reset).toHaveBeenCalledTimes(1); diff --git a/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.tsx b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.tsx index 231ef70fed5bf..5fbb81bd7e106 100644 --- a/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.tsx +++ b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.tsx @@ -204,8 +204,7 @@ export const EditUserPage: FunctionComponent = ({ username }) diff --git a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts index 96505d2b5f963..52702c014f0cb 100644 --- a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts +++ b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts @@ -23,11 +23,12 @@ import { Actions } from '../authorization'; import type { SavedObjectActions } from '../authorization/actions/saved_object'; import { SecureSavedObjectsClientWrapper } from './secure_saved_objects_client_wrapper'; -jest.mock('@kbn/core/server/saved_objects/service/lib/utils', () => { - const { SavedObjectsUtils } = jest.requireActual( - '@kbn/core/server/saved_objects/service/lib/utils' +jest.mock('@kbn/core-saved-objects-utils-server', () => { + const { SavedObjectsUtils, ...actual } = jest.requireActual( + '@kbn/core-saved-objects-utils-server' ); return { + ...actual, SavedObjectsUtils: { ...SavedObjectsUtils, createEmptyFindResponse: SavedObjectsUtils.createEmptyFindResponse, diff --git a/x-pack/plugins/security/server/user_profile/user_profile_service.test.ts b/x-pack/plugins/security/server/user_profile/user_profile_service.test.ts index 9254a46c0e8c5..53a8dea9ebb1a 100644 --- a/x-pack/plugins/security/server/user_profile/user_profile_service.test.ts +++ b/x-pack/plugins/security/server/user_profile/user_profile_service.test.ts @@ -466,21 +466,17 @@ describe('UserProfileService', () => { type: 'accessToken', accessToken: 'some-token', }); - await nextTick(); - jest.runAllTimers(); - - // The first retry. - await nextTick(); - jest.runAllTimers(); - // The second retry. - await nextTick(); - jest.runAllTimers(); + // Re-try 9 more times. + for (const _ of Array.from({ length: 9 })) { + await nextTick(); + jest.runAllTimers(); + } await expect(activatePromise).rejects.toBe(failureReason); expect( mockStartParams.clusterClient.asInternalUser.security.activateUserProfile - ).toHaveBeenCalledTimes(3); + ).toHaveBeenCalledTimes(10); expect( mockStartParams.clusterClient.asInternalUser.security.activateUserProfile ).toHaveBeenCalledWith({ grant_type: 'access_token', access_token: 'some-token' }); diff --git a/x-pack/plugins/security/server/user_profile/user_profile_service.ts b/x-pack/plugins/security/server/user_profile/user_profile_service.ts index 7be2abe07a24a..7abc9e41504fb 100644 --- a/x-pack/plugins/security/server/user_profile/user_profile_service.ts +++ b/x-pack/plugins/security/server/user_profile/user_profile_service.ts @@ -28,7 +28,7 @@ import { getPrintableSessionId } from '../session_management'; import type { UserProfileGrant } from './user_profile_grant'; const KIBANA_DATA_ROOT = 'kibana'; -const ACTIVATION_MAX_RETRIES = 3; +const ACTIVATION_MAX_RETRIES = 10; const ACTIVATION_RETRY_SCALE_DURATION_MS = 150; const MAX_SUGGESTIONS_COUNT = 100; const DEFAULT_SUGGESTIONS_COUNT = 10; diff --git a/x-pack/plugins/security_solution/common/search_strategy/common/index.ts b/x-pack/plugins/security_solution/common/search_strategy/common/index.ts index d8ef64fe1b5a4..c33c3f9abae6b 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/common/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/common/index.ts @@ -10,7 +10,6 @@ export type { SortField, TimerangeInput, PaginationInputPaginated, - DocValueFields, CursorType, TotalValue, } from '@kbn/timelines-plugin/common'; diff --git a/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts b/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts index 9750fbde2db47..01a1073c0fa71 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts @@ -14,8 +14,4 @@ export type { BrowserField, BrowserFields, } from '@kbn/timelines-plugin/common'; -export { - EMPTY_BROWSER_FIELDS, - EMPTY_DOCVALUE_FIELD, - EMPTY_INDEX_FIELDS, -} from '@kbn/timelines-plugin/common'; +export { EMPTY_BROWSER_FIELDS, EMPTY_INDEX_FIELDS } from '@kbn/timelines-plugin/common'; diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts index 43d3d8722e797..dfc26932e8d79 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { IEsSearchRequest } from '@kbn/data-plugin/common'; import type { ESQuery } from '../../typed_json'; import type { @@ -120,7 +119,6 @@ export interface RequestBasicOptions extends IEsSearchRequest { timerange: TimerangeInput; filterQuery: ESQuery | string | undefined; defaultIndex: string[]; - docValueFields?: estypes.QueryDslFieldAndFormat[]; factoryQueryType?: FactoryQueryTypes; } diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/custom_query_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/custom_query_rule.spec.ts index ba886993c7433..5725e511f2dec 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/custom_query_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/custom_query_rule.spec.ts @@ -23,7 +23,6 @@ import { RULES_TABLE, RULE_SWITCH, SEVERITY, - SHOWING_RULES_TEXT, } from '../../screens/alerts_detection_rules'; import { ABOUT_CONTINUE_BTN, @@ -218,7 +217,10 @@ describe('Custom query rules', () => { const initialNumberOfRules = rules.length; const expectedNumberOfRulesAfterDeletion = initialNumberOfRules - 1; - cy.get(SHOWING_RULES_TEXT).should('have.text', `Showing ${initialNumberOfRules} rules`); + cy.request({ url: '/api/detection_engine/rules/_find' }).then(({ body }) => { + const numberOfRules = body.data.length; + expect(numberOfRules).to.eql(initialNumberOfRules); + }); deleteFirstRule(); waitForRulesTableToBeRefreshed(); @@ -226,10 +228,10 @@ describe('Custom query rules', () => { cy.get(RULES_TABLE) .find(RULES_ROW) .should('have.length', expectedNumberOfRulesAfterDeletion); - cy.get(SHOWING_RULES_TEXT).should( - 'have.text', - `Showing ${expectedNumberOfRulesAfterDeletion} rules` - ); + cy.request({ url: '/api/detection_engine/rules/_find' }).then(({ body }) => { + const numberOfRules = body.data.length; + expect(numberOfRules).to.eql(expectedNumberOfRulesAfterDeletion); + }); cy.get(CUSTOM_RULES_BTN).should( 'have.text', `Custom rules (${expectedNumberOfRulesAfterDeletion})` @@ -253,10 +255,10 @@ describe('Custom query rules', () => { cy.get(RULES_TABLE) .find(RULES_ROW) .should('have.length', expectedNumberOfRulesAfterDeletion); - cy.get(SHOWING_RULES_TEXT).should( - 'have.text', - `Showing ${expectedNumberOfRulesAfterDeletion} rule` - ); + cy.request({ url: '/api/detection_engine/rules/_find' }).then(({ body }) => { + const numberOfRules = body.data.length; + expect(numberOfRules).to.eql(expectedNumberOfRulesAfterDeletion); + }); cy.get(CUSTOM_RULES_BTN).should( 'have.text', `Custom rules (${expectedNumberOfRulesAfterDeletion})` @@ -281,10 +283,10 @@ describe('Custom query rules', () => { cy.get(RULES_TABLE) .find(RULES_ROW) .should('have.length', expectedNumberOfRulesAfterDeletion); - cy.get(SHOWING_RULES_TEXT).should( - 'have.text', - `Showing ${expectedNumberOfRulesAfterDeletion} rules` - ); + cy.request({ url: '/api/detection_engine/rules/_find' }).then(({ body }) => { + const numberOfRules = body.data.length; + expect(numberOfRules).to.eql(expectedNumberOfRulesAfterDeletion); + }); cy.get(CUSTOM_RULES_BTN).should( 'have.text', `Custom rules (${expectedNumberOfRulesAfterDeletion})` diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/prebuilt_rules.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/prebuilt_rules.spec.ts index 397162f69d490..ed5f5629985ae 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/prebuilt_rules.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/prebuilt_rules.spec.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { rawRules } from '../../../server/lib/detection_engine/rules/prepackaged_rules'; import { COLLAPSED_ACTION_BTN, ELASTIC_RULES_BTN, @@ -12,11 +13,10 @@ import { RELOAD_PREBUILT_RULES_BTN, RULES_EMPTY_PROMPT, RULE_SWITCH, - SHOWING_RULES_TEXT, RULES_MONITORING_TABLE, SELECT_ALL_RULES_ON_PAGE_CHECKBOX, + RULE_NAME, } from '../../screens/alerts_detection_rules'; - import { deleteFirstRule, deleteSelectedRules, @@ -59,7 +59,16 @@ describe('Prebuilt rules', () => { changeRowsPerPageTo(rowsPerPage); - cy.get(SHOWING_RULES_TEXT).should('have.text', `Showing ${expectedNumberOfRules} rules`); + cy.request({ url: '/api/detection_engine/rules/_find' }).then(({ body }) => { + // Assert the total number of loaded rules equals the expected number of in-memory rules + expect(body.total).to.equal(rawRules.length); + // Assert the table was refreshed with the rules returned by the API request + const ruleNames = rawRules.map((rule) => rule.name); + cy.get(RULE_NAME).each(($item) => { + expect($item.text()).to.be.oneOf(ruleNames); + }); + }); + cy.get(pageSelector(expectedNumberOfPages)).should('exist'); }); diff --git a/x-pack/plugins/security_solution/public/app/home/global_header/index.test.tsx b/x-pack/plugins/security_solution/public/app/home/global_header/index.test.tsx index ff082a27e575e..48f1109e33443 100644 --- a/x-pack/plugins/security_solution/public/app/home/global_header/index.test.tsx +++ b/x-pack/plugins/security_solution/public/app/home/global_header/index.test.tsx @@ -48,7 +48,7 @@ jest.mock('../../../common/containers/sourcerer/use_signal_helpers', () => ({ jest.mock('react-reverse-portal', () => ({ InPortal: ({ children }: { children: React.ReactNode }) => <>{children}, OutPortal: ({ children }: { children: React.ReactNode }) => <>{children}, - createPortalNode: () => ({ unmount: jest.fn() }), + createHtmlPortalNode: () => ({ unmount: jest.fn() }), })); describe('global header', () => { diff --git a/x-pack/plugins/security_solution/public/app/home/global_header/index.tsx b/x-pack/plugins/security_solution/public/app/home/global_header/index.tsx index e585cf18ca156..08b3888130836 100644 --- a/x-pack/plugins/security_solution/public/app/home/global_header/index.tsx +++ b/x-pack/plugins/security_solution/public/app/home/global_header/index.tsx @@ -12,7 +12,7 @@ import { } from '@elastic/eui'; import React, { useCallback, useEffect, useMemo } from 'react'; import { useLocation } from 'react-router-dom'; -import { createPortalNode, InPortal, OutPortal } from 'react-reverse-portal'; +import { createHtmlPortalNode, InPortal, OutPortal } from 'react-reverse-portal'; import { i18n } from '@kbn/i18n'; import type { AppMountParameters } from '@kbn/core/public'; @@ -39,7 +39,7 @@ const BUTTON_ADD_DATA = i18n.translate('xpack.securitySolution.globalHeader.butt */ export const GlobalHeader = React.memo( ({ setHeaderActionMenu }: { setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'] }) => { - const portalNode = useMemo(() => createPortalNode(), []); + const portalNode = useMemo(() => createHtmlPortalNode(), []); const { theme, http: { diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.test.tsx index 94c2aa6bc9e8d..b87c96c34632a 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/threat_summary_view.test.tsx @@ -24,7 +24,7 @@ jest.mock('../../../lib/kibana', () => ({ }), })); -jest.mock('../table/action_cell'); +jest.mock('../table/action_cell', () => ({ ActionCell: () => <> })); jest.mock('../table/field_name_cell'); describe('ThreatSummaryView', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx index cb4affec563d5..eb034eca172dc 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx @@ -6,6 +6,7 @@ */ import { waitFor } from '@testing-library/dom'; +import { mount } from 'enzyme'; import type { ReactWrapper } from 'enzyme'; import React from 'react'; @@ -15,7 +16,6 @@ import { mockDetailItemData, mockDetailItemDataId, rawEventData, TestProviders } import { EventDetails, EventsViewType } from './event_details'; import { mockBrowserFields } from '../../containers/source/mock'; -import { useMountAppended } from '../../utils/use_mount_appended'; import { mockAlertDetailsData } from './__mocks__'; import type { TimelineEventsDetailsItem } from '../../../../common/search_strategy'; import { TimelineTabs } from '../../../../common/types/timeline'; @@ -44,7 +44,6 @@ jest.mock('../../../detections/containers/detection_engine/rules/use_rule_with_f jest.mock('../link_to'); describe('EventDetails', () => { - const mount = useMountAppended(); const defaultProps = { browserFields: mockBrowserFields, data: mockDetailItemData, diff --git a/x-pack/plugins/security_solution/public/common/components/filters_global/__snapshots__/filters_global.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/filters_global/__snapshots__/filters_global.test.tsx.snap index 4dd14f56997eb..f6734aa75add9 100644 --- a/x-pack/plugins/security_solution/public/common/components/filters_global/__snapshots__/filters_global.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/filters_global/__snapshots__/filters_global.test.tsx.snap @@ -2,7 +2,16 @@ exports[`rendering renders correctly 1`] = ` } + node={ + Object { + "element":
, + "elementType": "html", + "getInitialPortalProps": [Function], + "mount": [Function], + "setPortalProps": [Function], + "unmount": [Function], + } + } > ` export const MatrixHistogramComponent: React.FC = ({ chartHeight, defaultStackByOption, - docValueFields, endDate, errorMessage, filterQuery, @@ -176,7 +175,6 @@ export const MatrixHistogramComponent: React.FC = stackByField: selectedStackByOption.value, runtimeMappings, isPtrIncluded, - docValueFields, skip: querySkip, }; const [loading, { data, inspect, totalCount, refetch }] = diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts index 9557e2be55742..49ac62cb572e4 100644 --- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts @@ -15,7 +15,6 @@ import type { InputsModelId } from '../../store/inputs/constants'; import type { MatrixHistogramType } from '../../../../common/search_strategy/security_solution'; import type { UpdateDateRange } from '../charts/common'; import type { GlobalTimeArgs } from '../../containers/use_global_time'; -import type { DocValueFields } from '../../../../common/search_strategy'; import type { FieldValueThreshold } from '../../../detections/components/rules/threshold_input'; import type { GetLensAttributes, LensAttributes } from '../visualization_actions/types'; @@ -66,7 +65,6 @@ interface MatrixHistogramBasicProps { } export interface MatrixHistogramQueryProps { - docValueFields?: DocValueFields[]; endDate: string; errorMessage: string; indexNames: string[]; diff --git a/x-pack/plugins/security_solution/public/common/containers/alerts/use_alert_prevalence_from_process_tree.ts b/x-pack/plugins/security_solution/public/common/containers/alerts/use_alert_prevalence_from_process_tree.ts index 4ea3ff08c2abd..1a59271614c57 100644 --- a/x-pack/plugins/security_solution/public/common/containers/alerts/use_alert_prevalence_from_process_tree.ts +++ b/x-pack/plugins/security_solution/public/common/containers/alerts/use_alert_prevalence_from_process_tree.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { useHttp } from '../../lib/kibana'; import { useTimelineDataFilters } from '../../../timelines/containers/use_timeline_data_filters'; diff --git a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts index 8fef932c991a4..c770713b602b7 100644 --- a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts +++ b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts @@ -6,7 +6,7 @@ */ import deepEqual from 'fast-deep-equal'; -import { getOr, isEmpty, noop } from 'lodash/fp'; +import { getOr, noop } from 'lodash/fp'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Subscription } from 'rxjs'; @@ -48,7 +48,6 @@ export interface UseMatrixHistogramArgs { } export const useMatrixHistogram = ({ - docValueFields, endDate, errorMessage, filterQuery, @@ -88,7 +87,6 @@ export const useMatrixHistogram = ({ runtimeMappings, threshold, ...(isPtrIncluded != null ? { isPtrIncluded } : {}), - ...(!isEmpty(docValueFields) ? { docValueFields } : {}), ...(includeMissingData != null ? { includeMissingData } : {}), }); const { addError, addWarning } = useAppToasts(); @@ -171,7 +169,6 @@ export const useMatrixHistogram = ({ stackByField, threshold, ...(isPtrIncluded != null ? { isPtrIncluded } : {}), - ...(!isEmpty(docValueFields) ? { docValueFields } : {}), }; if (!deepEqual(prevRequest, myRequest)) { return myRequest; @@ -187,7 +184,6 @@ export const useMatrixHistogram = ({ histogramType, threshold, isPtrIncluded, - docValueFields, ]); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/common/containers/query_client/query_client_provider.tsx b/x-pack/plugins/security_solution/public/common/containers/query_client/query_client_provider.tsx index 20fd3d09ad593..7feaf9c8653ef 100644 --- a/x-pack/plugins/security_solution/public/common/containers/query_client/query_client_provider.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/query_client/query_client_provider.tsx @@ -7,7 +7,7 @@ import type { PropsWithChildren } from 'react'; import React, { memo, useMemo } from 'react'; -import { QueryClient, QueryClientProvider } from 'react-query'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; type QueryClientOptionsProp = ConstructorParameters[0]; diff --git a/x-pack/plugins/security_solution/public/common/containers/source/index.test.tsx b/x-pack/plugins/security_solution/public/common/containers/source/index.test.tsx index 24d58a665177b..40a473de30687 100644 --- a/x-pack/plugins/security_solution/public/common/containers/source/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/source/index.test.tsx @@ -102,7 +102,6 @@ describe('source/index.tsx', () => { expect(payload.id).toEqual('neato'); expect(Object.keys(payload.browserFields)).toHaveLength(12); expect(Object.keys(payload.indexFields)).toHaveLength(mocksSource.indexFields.length); - expect(payload.docValueFields).toEqual([{ field: '@timestamp' }]); }); it('should reuse the result for dataView info when cleanCache not passed', async () => { @@ -120,23 +119,18 @@ describe('source/index.tsx', () => { await indexFieldsSearch!({ dataViewId: 'neato' }); const { - payload: { browserFields, indexFields, docValueFields }, + payload: { browserFields, indexFields }, } = mockDispatch.mock.calls[1][0]; mockDispatch.mockClear(); await indexFieldsSearch!({ dataViewId: 'neato' }); const { - payload: { - browserFields: newBrowserFields, - indexFields: newIndexFields, - docValueFields: newDocValueFields, - }, + payload: { browserFields: newBrowserFields, indexFields: newIndexFields }, } = mockDispatch.mock.calls[1][0]; expect(browserFields).toBe(newBrowserFields); expect(indexFields).toBe(newIndexFields); - expect(docValueFields).toBe(newDocValueFields); }); it('should not reuse the result for dataView info when cleanCache passed', async () => { @@ -154,23 +148,18 @@ describe('source/index.tsx', () => { await indexFieldsSearch!({ dataViewId: 'neato' }); const { - payload: { browserFields, indexFields, docValueFields }, + payload: { browserFields, indexFields }, } = mockDispatch.mock.calls[1][0]; mockDispatch.mockClear(); await indexFieldsSearch!({ dataViewId: 'neato', cleanCache: true }); const { - payload: { - browserFields: newBrowserFields, - indexFields: newIndexFields, - docValueFields: newDocValueFields, - }, + payload: { browserFields: newBrowserFields, indexFields: newIndexFields }, } = mockDispatch.mock.calls[1][0]; expect(browserFields).not.toBe(newBrowserFields); expect(indexFields).not.toBe(newIndexFields); - expect(docValueFields).not.toBe(newDocValueFields); }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/containers/source/index.tsx b/x-pack/plugins/security_solution/public/common/containers/source/index.tsx index 68beb34712a6e..7f420fbf085da 100644 --- a/x-pack/plugins/security_solution/public/common/containers/source/index.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/source/index.tsx @@ -15,7 +15,6 @@ import { Subscription } from 'rxjs'; import type { BrowserField, BrowserFields, - DocValueFields, IndexField, IndexFieldsStrategyRequest, IndexFieldsStrategyResponse, @@ -26,7 +25,7 @@ import * as i18n from './translations'; import { useAppToasts } from '../../hooks/use_app_toasts'; import { getDataViewStateFromIndexFields } from './use_data_view'; -export type { BrowserField, BrowserFields, DocValueFields }; +export type { BrowserField, BrowserFields }; export function getAllBrowserFields(browserFields: BrowserFields): Array> { const result: Array> = []; @@ -86,11 +85,8 @@ export const getBrowserFields = memoizeOne( const DEFAULT_BROWSER_FIELDS = {}; const DEFAULT_INDEX_PATTERNS = { fields: [], title: '' }; -const DEFAULT_DOC_VALUE_FIELDS: DocValueFields[] = []; - interface FetchIndexReturn { browserFields: BrowserFields; - docValueFields: DocValueFields[]; indexes: string[]; indexExists: boolean; indexPatterns: DataViewBase; @@ -112,7 +108,6 @@ export const useFetchIndex = ( const [state, setState] = useState({ browserFields: DEFAULT_BROWSER_FIELDS, - docValueFields: DEFAULT_DOC_VALUE_FIELDS, indexes: indexNames, indexExists: true, indexPatterns: DEFAULT_INDEX_PATTERNS, @@ -140,14 +135,13 @@ export const useFetchIndex = ( const stringifyIndices = response.indicesExist.sort().join(); previousIndexesName.current = response.indicesExist; - const { browserFields, docValueFields } = getDataViewStateFromIndexFields( + const { browserFields } = getDataViewStateFromIndexFields( stringifyIndices, response.indexFields ); setLoading(false); setState({ browserFields, - docValueFields, indexes: response.indicesExist, indexExists: response.indicesExist.length > 0, indexPatterns: getIndexFields(stringifyIndices, response.indexFields), diff --git a/x-pack/plugins/security_solution/public/common/containers/source/mock.ts b/x-pack/plugins/security_solution/public/common/containers/source/mock.ts index 607225ff62e57..956275d43bac7 100644 --- a/x-pack/plugins/security_solution/public/common/containers/source/mock.ts +++ b/x-pack/plugins/security_solution/public/common/containers/source/mock.ts @@ -7,7 +7,6 @@ import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { DEFAULT_INDEX_PATTERN } from '../../../../common/constants'; -import type { DocValueFields } from '../../../../common/search_strategy'; import type { BrowserFields } from '../../../../common/search_strategy/index_fields'; export const mocksSource = { @@ -957,17 +956,6 @@ export const mockBrowserFields: BrowserFields = { }, }; -export const mockDocValueFields: DocValueFields[] = [ - { - field: '@timestamp', - format: 'date_time', - }, - { - field: 'event.end', - format: 'date_time', - }, -]; - export const mockRuntimeMappings: MappingRuntimeFields = { '@a.runtime.field': { script: { diff --git a/x-pack/plugins/security_solution/public/common/containers/source/use_data_view.tsx b/x-pack/plugins/security_solution/public/common/containers/source/use_data_view.tsx index 49cf89e0de1b0..51ad895b56f0c 100644 --- a/x-pack/plugins/security_solution/public/common/containers/source/use_data_view.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/source/use_data_view.tsx @@ -12,7 +12,6 @@ import memoizeOne from 'memoize-one'; import { omit, pick } from 'lodash/fp'; import type { BrowserField, - DocValueFields, IndexField, IndexFieldsStrategyRequest, IndexFieldsStrategyResponse, @@ -40,7 +39,6 @@ type DangerCastForBrowserFieldsMutation = Record< >; interface DataViewInfo { browserFields: DangerCastForBrowserFieldsMutation; - docValueFields: DocValueFields[]; indexFields: FieldSpec[]; } @@ -69,17 +67,10 @@ export const getDataViewStateFromIndexFields = memoizeOne( pick(['name', 'searchable', 'type', 'aggregatable', 'esTypes', 'subType'], field) ); - // mutate docValueFields - if (field.readFromDocValues && acc.docValueFields.length < 100) { - acc.docValueFields.push({ - field: field.name, - }); - } return acc; }, { browserFields: {}, - docValueFields: [], indexFields: [], } ); diff --git a/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx b/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx index effd5196dd9bc..6db7392b596b7 100644 --- a/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx @@ -408,7 +408,6 @@ export const useSourcererDataView = ( () => ({ browserFields: sourcererDataView.browserFields, dataViewId: sourcererDataView.id, - docValueFields: sourcererDataView.docValueFields, indexPattern: { fields: sourcererDataView.indexFields, title: selectedPatterns.join(','), diff --git a/x-pack/plugins/security_solution/public/common/containers/sourcerer/readme.md b/x-pack/plugins/security_solution/public/common/containers/sourcerer/readme.md index d6edb9794dc8b..b8672d7a9edc7 100644 --- a/x-pack/plugins/security_solution/public/common/containers/sourcerer/readme.md +++ b/x-pack/plugins/security_solution/public/common/containers/sourcerer/readme.md @@ -16,7 +16,6 @@ interface SelectedDataView { browserFields: SourcererDataView['browserFields']; dataViewId: string | null; // null if legacy pre-8.0 timeline - docValueFields: SourcererDataView['docValueFields']; /** * DataViewBase with enhanced index fields used in timelines */ diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_global_header_portal.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_global_header_portal.tsx index 8e8d73ff12849..c240f102d5e67 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_global_header_portal.tsx +++ b/x-pack/plugins/security_solution/public/common/hooks/use_global_header_portal.tsx @@ -6,12 +6,12 @@ */ import { useState } from 'react'; -import { createPortalNode } from 'react-reverse-portal'; +import { createHtmlPortalNode } from 'react-reverse-portal'; /** * A singleton portal for rendering content in the global header */ -const globalKQLHeaderPortalNodeSingleton = createPortalNode(); +const globalKQLHeaderPortalNodeSingleton = createHtmlPortalNode(); export const useGlobalHeaderPortal = () => { const [globalKQLHeaderPortalNode] = useState(globalKQLHeaderPortalNodeSingleton); diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_timeline_events_count.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_timeline_events_count.tsx index e801db0190799..e6e744fdd4b2e 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_timeline_events_count.tsx +++ b/x-pack/plugins/security_solution/public/common/hooks/use_timeline_events_count.tsx @@ -6,13 +6,13 @@ */ import React, { useState } from 'react'; -import { createPortalNode, OutPortal } from 'react-reverse-portal'; +import { createHtmlPortalNode, OutPortal } from 'react-reverse-portal'; /** * A singleton portal for rendering content in the global header */ -const timelineEventsCountPortalNodeSingleton = createPortalNode(); -const eqlEventsCountPortalNodeSingleton = createPortalNode(); +const timelineEventsCountPortalNodeSingleton = createHtmlPortalNode(); +const eqlEventsCountPortalNodeSingleton = createHtmlPortalNode(); export const useTimelineEventsCountPortal = () => { const [timelineEventsCountPortalNode] = useState(timelineEventsCountPortalNodeSingleton); diff --git a/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx b/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx index b4987a6080342..4ed453a4ce0a3 100644 --- a/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx +++ b/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx @@ -13,7 +13,7 @@ import type { RenderOptions, RenderResult } from '@testing-library/react'; import { render as reactRender } from '@testing-library/react'; import type { Action, Reducer, Store } from 'redux'; import type { AppDeepLink } from '@kbn/core/public'; -import { QueryClient, QueryClientProvider, setLogger } from 'react-query'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { coreMock } from '@kbn/core/public/mocks'; import { PLUGIN_ID } from '@kbn/fleet-plugin/common'; import type { RenderHookOptions, RenderHookResult } from '@testing-library/react-hooks'; @@ -22,7 +22,7 @@ import type { ReactHooksRenderer, WrapperComponent, } from '@testing-library/react-hooks/src/types/react'; -import type { UseBaseQueryResult } from 'react-query/types/react/types'; +import type { UseBaseQueryResult } from '@tanstack/react-query'; import ReactDOM from 'react-dom'; import { ConsoleManager } from '../../../management/components/console'; import type { StartPlugins, StartServices } from '../../../types'; @@ -89,7 +89,6 @@ export type WaitForReactHookState = | 'isSuccess' | 'isLoading' | 'isError' - | 'isIdle' | 'isLoadingError' | 'isStale' | 'isFetched' @@ -117,15 +116,6 @@ export type ReactQueryHookRenderer< options?: RenderHookOptions ) => Promise; -// hide react-query output in console -setLogger({ - error: () => {}, - // eslint-disable-next-line no-console - log: console.log, - // eslint-disable-next-line no-console - warn: console.warn, -}); - /** * Mocked app root context renderer */ @@ -228,6 +218,14 @@ export const createAppRootMockRenderer = (): AppContextTestRender => { cacheTime: Infinity, }, }, + // hide react-query output in console + logger: { + error: () => {}, + // eslint-disable-next-line no-console + log: console.log, + // eslint-disable-next-line no-console + warn: console.warn, + }, }); const AppWrapper: React.FC<{ children: React.ReactElement }> = ({ children }) => ( diff --git a/x-pack/plugins/security_solution/public/common/mock/global_state.ts b/x-pack/plugins/security_solution/public/common/mock/global_state.ts index 027827474c780..17f5e4d9be8f8 100644 --- a/x-pack/plugins/security_solution/public/common/mock/global_state.ts +++ b/x-pack/plugins/security_solution/public/common/mock/global_state.ts @@ -34,12 +34,7 @@ import type { ManagementState } from '../../management/types'; import { initialSourcererState, SourcererScopeName } from '../store/sourcerer/model'; import { allowedExperimentalValues } from '../../../common/experimental_features'; import { getScopePatternListSelection } from '../store/sourcerer/helpers'; -import { - mockBrowserFields, - mockDocValueFields, - mockIndexFields, - mockRuntimeMappings, -} from '../containers/source/mock'; +import { mockBrowserFields, mockIndexFields, mockRuntimeMappings } from '../containers/source/mock'; import { usersModel } from '../../users/store'; import { UsersFields } from '../../../common/search_strategy/security_solution/users/common'; @@ -49,7 +44,6 @@ export const mockSourcererState = { defaultDataView: { ...initialSourcererState.defaultDataView, browserFields: mockBrowserFields, - docValueFields: mockDocValueFields, id: DEFAULT_DATA_VIEW_ID, indexFields: mockIndexFields, loading: false, diff --git a/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx b/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx index 41d68949daccb..3907232cc1120 100644 --- a/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx +++ b/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx @@ -16,7 +16,7 @@ import type { Store } from 'redux'; import { BehaviorSubject } from 'rxjs'; import { ThemeProvider } from 'styled-components'; import type { Capabilities } from '@kbn/core/public'; -import { QueryClient, QueryClientProvider } from 'react-query'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { ConsoleManager } from '../../management/components/console'; import type { State } from '../store'; diff --git a/x-pack/plugins/security_solution/public/common/store/sourcerer/actions.ts b/x-pack/plugins/security_solution/public/common/store/sourcerer/actions.ts index 2abd8a7a50919..f452d34cba310 100644 --- a/x-pack/plugins/security_solution/public/common/store/sourcerer/actions.ts +++ b/x-pack/plugins/security_solution/public/common/store/sourcerer/actions.ts @@ -19,7 +19,6 @@ const actionCreator = actionCreatorFactory('x-pack/security_solution/local/sourc export const setDataView = actionCreator<{ browserFields: SourcererDataView['browserFields']; - docValueFields: SourcererDataView['docValueFields']; id: SourcererDataView['id']; indexFields: SourcererDataView['indexFields']; loading: SourcererDataView['loading']; diff --git a/x-pack/plugins/security_solution/public/common/store/sourcerer/model.ts b/x-pack/plugins/security_solution/public/common/store/sourcerer/model.ts index 3b35db56f4d0b..e4d16f2079dba 100644 --- a/x-pack/plugins/security_solution/public/common/store/sourcerer/model.ts +++ b/x-pack/plugins/security_solution/public/common/store/sourcerer/model.ts @@ -6,12 +6,8 @@ */ import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import type { BrowserFields, DocValueFields } from '@kbn/timelines-plugin/common'; -import { - EMPTY_BROWSER_FIELDS, - EMPTY_DOCVALUE_FIELD, - EMPTY_INDEX_FIELDS, -} from '@kbn/timelines-plugin/common'; +import type { BrowserFields } from '@kbn/timelines-plugin/common'; +import { EMPTY_BROWSER_FIELDS, EMPTY_INDEX_FIELDS } from '@kbn/timelines-plugin/common'; import type { SecuritySolutionDataViewBase } from '../../types'; /** Uniquely identifies a Sourcerer Scope */ export enum SourcererScopeName { @@ -64,8 +60,6 @@ export interface SourcererDataView extends KibanaDataView { * category, description, format * indices the field is included in etc*/ browserFields: BrowserFields; - /** query DSL field and format */ - docValueFields: DocValueFields[]; /** comes from dataView.fields.toSpec() */ indexFields: SecuritySolutionDataViewBase['fields']; /** set when data view fields are fetched */ @@ -84,7 +78,6 @@ export interface SourcererDataView extends KibanaDataView { export interface SelectedDataView { browserFields: SourcererDataView['browserFields']; dataViewId: string | null; // null if legacy pre-8.0 timeline - docValueFields: SourcererDataView['docValueFields']; /** * DataViewBase with enhanced index fields used in timelines */ @@ -131,7 +124,6 @@ export const initSourcererScope: Omit = { }; export const initDataView = { browserFields: EMPTY_BROWSER_FIELDS, - docValueFields: EMPTY_DOCVALUE_FIELD, id: '', indexFields: EMPTY_INDEX_FIELDS, loading: false, diff --git a/x-pack/plugins/security_solution/public/common/store/sourcerer/readme.md b/x-pack/plugins/security_solution/public/common/store/sourcerer/readme.md index 6dbc8f5ad817c..28059371804c9 100644 --- a/x-pack/plugins/security_solution/public/common/store/sourcerer/readme.md +++ b/x-pack/plugins/security_solution/public/common/store/sourcerer/readme.md @@ -69,7 +69,6 @@ interface SourcererDataView extends KibanaDataView { * indices the field is included in etc*/ browserFields: BrowserFields; /** query DSL field and format */ - docValueFields: DocValueFields[]; /** comes from dataView.fields.toSpec() */ indexFields: SecuritySolutionDataViewBase['fields']; /** set when data view fields are fetched */ diff --git a/x-pack/plugins/security_solution/public/common/utils/alerts.ts b/x-pack/plugins/security_solution/public/common/utils/alerts.ts index 461adbcc6e8c1..e0d497ba9e3f8 100644 --- a/x-pack/plugins/security_solution/public/common/utils/alerts.ts +++ b/x-pack/plugins/security_solution/public/common/utils/alerts.ts @@ -9,7 +9,6 @@ import { merge } from '@kbn/std'; import { isPlainObject } from 'lodash'; import type { Ecs } from '@kbn/cases-plugin/common'; -// TODO we need to allow -> docValueFields: [{ field: "@timestamp" }], export const buildAlertsQuery = (alertIds: string[]) => { if (alertIds.length === 0) { return {}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_events_table/use_execution_events.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_events_table/use_execution_events.test.tsx index 9a62fd58a0fbc..5e07dadf02492 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_events_table/use_execution_events.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_events_table/use_execution_events.test.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { QueryClient, QueryClientProvider } from 'react-query'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { renderHook, cleanup } from '@testing-library/react-hooks'; import { diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_events_table/use_execution_events.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_events_table/use_execution_events.ts index 4a37f55dc1d6a..8c0a698eac43f 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_events_table/use_execution_events.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_events_table/use_execution_events.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; import type { GetRuleExecutionEventsResponse } from '../../../../../common/detection_engine/rule_monitoring'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.test.tsx index 63565f7cfa1b5..ddb553998584f 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.test.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { QueryClient, QueryClientProvider } from 'react-query'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { renderHook, cleanup } from '@testing-library/react-hooks'; import { useExecutionResults } from './use_execution_results'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.tsx index a07289969af12..001c0bc153961 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; import type { GetRuleExecutionResultsResponse } from '../../../../../common/detection_engine/rule_monitoring'; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/add_item_form/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/add_item_form/index.tsx index 68ba032175e3a..48c306a36a2eb 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/add_item_form/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/add_item_form/index.tsx @@ -99,7 +99,6 @@ export const AddItem = ({ const updateItem = useCallback( (event: ChangeEvent, index: number) => { - event.persist(); const values = field.value as string[]; const value = event.target.value; field.setValue([...values.slice(0, index), value, ...values.slice(index + 1)]); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/related_integrations/use_installed_integrations.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/related_integrations/use_installed_integrations.test.tsx index 9ce7134db5f2a..705c7f5f3fbd3 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/related_integrations/use_installed_integrations.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/related_integrations/use_installed_integrations.test.tsx @@ -9,7 +9,7 @@ jest.mock('../../../containers/detection_engine/rules/api'); jest.mock('../../../../common/lib/kibana'); import React from 'react'; -import { QueryClient, QueryClientProvider } from 'react-query'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { renderHook, cleanup } from '@testing-library/react-hooks'; import { useInstalledIntegrations } from './use_installed_integrations'; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/related_integrations/use_installed_integrations.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/related_integrations/use_installed_integrations.tsx index bc9e837911233..734ef6e628214 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/related_integrations/use_installed_integrations.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/related_integrations/use_installed_integrations.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import type { InstalledIntegrationArray } from '../../../../../common/detection_engine/schemas/common'; import { fetchInstalledIntegrations } from '../../../containers/detection_engine/rules/api'; // import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx index 1cccca810f7e0..c0698ba443d8b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx @@ -150,7 +150,7 @@ export const RuleActionsField: React.FC = ({ field, messageVariables }) = <> - + {fieldErrors} diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.test.tsx index 8e8c6f11975f5..8b736bad37b92 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.test.tsx @@ -11,6 +11,7 @@ import React from 'react'; import { goToRuleEditPage, executeRulesBulkAction, + bulkExportRules, } from '../../../pages/detection_engine/rules/all/actions'; import { RuleActionsOverflow } from '.'; import { mockRule } from '../../../pages/detection_engine/rules/all/__mocks__/mock'; @@ -33,6 +34,8 @@ jest.mock('../../../../common/lib/kibana', () => { jest.mock('../../../pages/detection_engine/rules/all/actions'); const executeRulesBulkActionMock = executeRulesBulkAction as jest.Mock; +const bulkExportRulesMock = bulkExportRules as jest.Mock; + const flushPromises = () => new Promise(setImmediate); describe('RuleActionsOverflow', () => { @@ -233,6 +236,28 @@ describe('RuleActionsOverflow', () => { }); describe('rules details export rule', () => { + test('should call export actions and display toast when export option is clicked', async () => { + bulkExportRulesMock.mockImplementation(() => Promise.resolve({})); + const wrapper = mount( + + ); + wrapper.find('[data-test-subj="rules-details-popover-button-icon"] button').simulate('click'); + wrapper.update(); + wrapper.find('[data-test-subj="rules-details-export-rule"] button').simulate('click'); + wrapper.update(); + await flushPromises(); + + expect(bulkExportRulesMock).toHaveBeenCalledWith( + expect.objectContaining({ action: 'export' }) + ); + expect(bulkExportRulesMock).toHaveBeenCalledWith( + expect.not.objectContaining({ onSuccess: expect.any }) + ); + }); test('it does not open the popover when rules-details-popover-button-icon is clicked and the user does not have permission', () => { const rule = mockRule('id'); const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx index 27b776e67c61f..b8e423827edce 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx @@ -111,7 +111,6 @@ const RuleActionsOverflowComponent = ({ closePopover(); await bulkExportRules({ action: BulkAction.export, - onSuccess: noop, search: { ids: [rule.id] }, toasts, }); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/helpers.ts b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/helpers.ts index 9ee6628636ad4..266c0185745af 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/helpers.ts +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/helpers.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { isEmpty } from 'lodash'; import { Position, ScaleType } from '@elastic/charts'; import type { EuiSelectOption } from '@elastic/eui'; import type { Type, Language, ThreatMapping } from '@kbn/securitysolution-io-ts-alerting-types'; @@ -207,7 +208,7 @@ export const getIsRulePreviewDisabled = ({ return machineLearningJobId.length === 0; } if (ruleType === 'eql' || ruleType === 'query' || ruleType === 'threshold') { - return queryBar.query.query.length === 0; + return isEmpty(queryBar.query.query) && isEmpty(queryBar.filters); } if (ruleType === 'new_terms') { return newTermsFields.length === 0; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx index b1c55bdecc52d..f9be710725986 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx @@ -615,7 +615,6 @@ const StepDefineRuleComponent: FC = ({ component={QueryBarDefineRule} componentProps={{ browserFields, - // docValueFields, // runtimeMappings, idAria: 'detectionEngineStepDefineRuleQueryBar', indexPattern, diff --git a/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx index 01e676189f79e..6a83df5a87d19 100644 --- a/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx @@ -474,14 +474,13 @@ describe('take action dropdown', () => { await waitFor(() => { expect(apiMocks.responseProvider.metadataDetails).toHaveBeenCalled(); - }); - - wrapper.update(); + wrapper.update(); - expect(findLaunchResponderButton().first().prop('disabled')).toBe(true); - expect(findLaunchResponderButton().first().prop('toolTipContent')).toEqual( - HOST_ENDPOINT_UNENROLLED_TOOLTIP - ); + expect(findLaunchResponderButton().first().prop('disabled')).toBe(true); + expect(findLaunchResponderButton().first().prop('toolTipContent')).toEqual( + HOST_ENDPOINT_UNENROLLED_TOOLTIP + ); + }); }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_find_rules_query.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_find_rules_query.ts index e78be9ca1c753..523a05012ca19 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_find_rules_query.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_find_rules_query.ts @@ -6,8 +6,8 @@ */ import { useCallback } from 'react'; -import type { UseQueryOptions } from 'react-query'; -import { useQuery, useQueryClient } from 'react-query'; +import type { UseQueryOptions } from '@tanstack/react-query'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; import { fetchRules } from './api'; import * as i18n from './translations'; @@ -78,9 +78,8 @@ export const useInvalidateRules = () => { * Invalidate all queries that start with FIND_RULES_QUERY_KEY. This * includes the in-memory query cache and paged query cache. */ - queryClient.invalidateQueries(FIND_RULES_QUERY_KEY, { - refetchActive: true, - refetchInactive: false, + queryClient.invalidateQueries([FIND_RULES_QUERY_KEY], { + refetchType: 'active', }); }, [queryClient]); }; @@ -104,7 +103,7 @@ export const useUpdateRulesCache = () => { return useCallback( (newRules: Rule[]) => { queryClient.setQueriesData['data']>( - FIND_RULES_QUERY_KEY, + [FIND_RULES_QUERY_KEY], (currentData) => currentData ? { diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_install_pre_packaged_rules.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_install_pre_packaged_rules.ts index ff3493037182c..3f56cac04fb02 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_install_pre_packaged_rules.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_install_pre_packaged_rules.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { useMutation } from 'react-query'; +import { useMutation } from '@tanstack/react-query'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; import { createPrepackagedRules } from './api'; import * as i18n from './translations'; diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules.test.tsx index f977e15626c16..5cddbeef63028 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules.test.tsx @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - import { act, renderHook } from '@testing-library/react-hooks'; import { shallow } from 'enzyme'; import type { ReactElement } from 'react'; @@ -144,6 +143,7 @@ describe('usePrePackagedRules', () => { result.current.createPrePackagedRules(); await waitForNextUpdate(); expect(api.createPrepackagedRules).toHaveBeenCalled(); + await waitForNextUpdate(); expect(result.current).toEqual({ getLoadPrebuiltRulesAndTemplatesButton: result.current.getLoadPrebuiltRulesAndTemplatesButton, diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules_status.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules_status.ts index 02350d84ec99e..c01bce4fe8bc2 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules_status.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules_status.ts @@ -5,7 +5,7 @@ * 2.0. */ import { useCallback } from 'react'; -import { useQuery, useQueryClient } from 'react-query'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; import { getPrePackagedRulesStatus } from './api'; import * as i18n from './translations'; @@ -62,9 +62,8 @@ export const useInvalidatePrePackagedRulesStatus = () => { const queryClient = useQueryClient(); return useCallback(() => { - queryClient.invalidateQueries(PRE_PACKAGED_RULES_STATUS_QUERY_KEY, { - refetchActive: true, - refetchInactive: false, + queryClient.invalidateQueries([PRE_PACKAGED_RULES_STATUS_QUERY_KEY], { + refetchType: 'active', }); }, [queryClient]); }; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_actions_dry_run.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_actions_dry_run.ts index 4468b1c65ece0..28c4e4be608dc 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_actions_dry_run.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/bulk_actions/use_bulk_actions_dry_run.ts @@ -5,8 +5,8 @@ * 2.0. */ -import type { UseMutateAsyncFunction } from 'react-query'; -import { useMutation } from 'react-query'; +import type { UseMutateAsyncFunction } from '@tanstack/react-query'; +import { useMutation } from '@tanstack/react-query'; import type { BulkAction, diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.tsx index 415bbfd1498c7..d7908d0bbce66 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.tsx @@ -26,7 +26,7 @@ import { useFormatUrl } from '../../../../../../common/components/link_to'; import { Loader } from '../../../../../../common/components/loader'; import * as i18n from './translations'; -import { AllRulesUtilityBar } from '../utility_bar'; +import { ExceptionsTableUtilityBar } from './exceptions_table_utility_bar'; import type { AllExceptionListsColumns } from './columns'; import { getAllExceptionListsColumns } from './columns'; import { useAllExceptionLists } from './use_all_exception_lists'; @@ -378,11 +378,8 @@ export const ExceptionListsTable = React.memo(() => { ) : ( <> - diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table_utility_bar.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table_utility_bar.test.tsx new file mode 100644 index 0000000000000..d2bf2b8547f68 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table_utility_bar.test.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 { TestProviders } from '../../../../../../common/mock'; +import { render, screen, within } from '@testing-library/react'; +import { ExceptionsTableUtilityBar } from './exceptions_table_utility_bar'; + +describe('ExceptionsTableUtilityBar', () => { + it('displays correct exception lists label and refresh rules action button', () => { + const EXCEPTION_LISTS_NUMBER = 25; + render( + + + + ); + + expect(screen.getByTestId('showingExceptionLists')).toBeInTheDocument(); + expect(screen.getByTestId('refreshRulesAction')).toBeInTheDocument(); + expect(screen.getByText(`Showing ${EXCEPTION_LISTS_NUMBER} lists`)).toBeInTheDocument(); + }); + + it('invokes refresh on refresh action click', () => { + const mockRefresh = jest.fn(); + render( + + + + ); + + const buttonWrapper = screen.getByTestId('refreshRulesAction'); + within(buttonWrapper).getByRole('button').click(); + + expect(mockRefresh).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table_utility_bar.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table_utility_bar.tsx new file mode 100644 index 0000000000000..062b4b0fef8f9 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table_utility_bar.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 { + UtilityBar, + UtilityBarAction, + UtilityBarGroup, + UtilityBarSection, + UtilityBarText, +} from '../../../../../../common/components/utility_bar'; +import * as i18n from './translations'; + +interface ExceptionsTableUtilityBarProps { + onRefresh?: () => void; + totalExceptionLists: number; +} + +export const ExceptionsTableUtilityBar: React.FC = ({ + onRefresh, + totalExceptionLists, +}) => { + return ( + + + + + {i18n.SHOWING_EXCEPTION_LISTS(totalExceptionLists)} + + + + + {i18n.REFRESH_EXCEPTIONS_TABLE} + + + + + ); +}; + +ExceptionsTableUtilityBar.displayName = 'ExceptionsTableUtilityBar'; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/translations.ts index 004b6c5d97bec..b22d4030384a3 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/translations.ts @@ -28,6 +28,15 @@ export const EXCEPTION_LIST_ACTIONS = i18n.translate( } ); +export const SHOWING_EXCEPTION_LISTS = (totalLists: number) => + i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.all.exceptions.showingExceptionLists', + { + values: { totalLists }, + defaultMessage: 'Showing {totalLists} {totalLists, plural, =1 {list} other {lists}}', + } + ); + export const RULES_ASSIGNED_TO_TITLE = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.all.exceptions.rulesAssignedTitle', { @@ -151,3 +160,10 @@ export const EXCEPTION_LIST_SEARCH_PLACEHOLDER = i18n.translate( defaultMessage: 'e.g. Example List Name', } ); + +export const REFRESH_EXCEPTIONS_TABLE = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.all.exceptions.refresh', + { + defaultMessage: 'Refresh', + } +); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/utility_bar.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_utility_bar.test.tsx similarity index 52% rename from x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/utility_bar.test.tsx rename to x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_utility_bar.test.tsx index 91f4a0b06e71d..4fecfc4afa6a2 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/utility_bar.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_utility_bar.test.tsx @@ -9,67 +9,80 @@ import React from 'react'; import { mount } from 'enzyme'; import { waitFor } from '@testing-library/react'; -import { AllRulesUtilityBar } from './utility_bar'; +import { getShowingRulesParams, RulesTableUtilityBar } from './rules_table_utility_bar'; import { TestProviders } from '../../../../../common/mock'; -describe('AllRules', () => { - it('renders AllRulesUtilityBar total rules and selected rules', () => { +jest.mock('./rules_table/rules_table_context'); + +describe('RulesTableUtilityBar', () => { + it('renders RulesTableUtilityBar total rules and selected rules', () => { const wrapper = mount( - ); - expect(wrapper.find('[data-test-subj="showingRules"]').at(0).text()).toEqual('Showing 4 rules'); + expect(wrapper.find('[data-test-subj="showingRules"]').at(0).text()).toEqual( + 'Showing 1-10 of 21 rules' + ); expect(wrapper.find('[data-test-subj="selectedRules"]').at(0).text()).toEqual( 'Selected 1 rule' ); }); - it('does not render total selected and bulk actions when "hasBulkActions" is false', () => { + it('renders correct pagination label according to pagination data', () => { const wrapper = mount( - ); - - expect(wrapper.find('[data-test-subj="showingRules"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="tableBulkActions"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="showingExceptionLists"]').at(0).text()).toEqual( - 'Showing 4 lists' + expect(wrapper.find('[data-test-subj="showingRules"]').at(0).text()).toEqual( + 'Showing 1-10 of 21 rules' ); }); it('renders utility actions if user has permissions', () => { const wrapper = mount( - ); @@ -80,15 +93,19 @@ describe('AllRules', () => { it('renders no utility actions if user has no permissions', () => { const wrapper = mount( - ); @@ -100,15 +117,19 @@ describe('AllRules', () => { const mockRefresh = jest.fn(); const wrapper = mount( - ); @@ -122,15 +143,19 @@ describe('AllRules', () => { const mockSwitch = jest.fn(); const wrapper = mount( - ); @@ -146,15 +171,19 @@ describe('AllRules', () => { const mockSwitch = jest.fn(); const wrapper = mount( - ); @@ -165,4 +194,72 @@ describe('AllRules', () => { expect(mockSwitch).not.toHaveBeenCalled(); }); }); + + describe('getShowingRulesParams creates correct label when', () => { + it('there are 0 rules to display', () => { + const pagination = { + page: 1, + perPage: 10, + total: 0, + }; + const [firstInPage, lastInPage] = getShowingRulesParams(pagination); + expect(firstInPage).toEqual(0); + expect(lastInPage).toEqual(0); + }); + + it('there is 1 rule to display', () => { + const pagination = { + page: 1, + perPage: 10, + total: 1, + }; + const [firstInPage, lastInPage] = getShowingRulesParams(pagination); + expect(firstInPage).toEqual(1); + expect(lastInPage).toEqual(1); + }); + + it('the table displays the first page, and rules per page is less than total rules', () => { + const pagination = { + page: 1, + perPage: 10, + total: 21, + }; + const [firstInPage, lastInPage] = getShowingRulesParams(pagination); + expect(firstInPage).toEqual(1); + expect(lastInPage).toEqual(10); + }); + + it('the table displays the first page, and rules per page is greater than total rules', () => { + const pagination = { + page: 1, + perPage: 10, + total: 8, + }; + const [firstInPage, lastInPage] = getShowingRulesParams(pagination); + expect(firstInPage).toEqual(1); + expect(lastInPage).toEqual(8); + }); + + it('the table displays the second page, and rules per page is less than total rules', () => { + const pagination = { + page: 2, + perPage: 10, + total: 31, + }; + const [firstInPage, lastInPage] = getShowingRulesParams(pagination); + expect(firstInPage).toEqual(11); + expect(lastInPage).toEqual(20); + }); + + it('the table displays the last page, displaying the remaining rules', () => { + const pagination = { + page: 2, + perPage: 100, + total: 101, + }; + const [firstInPage, lastInPage] = getShowingRulesParams(pagination); + expect(firstInPage).toEqual(101); + expect(lastInPage).toEqual(101); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/utility_bar.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_utility_bar.tsx similarity index 58% rename from x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/utility_bar.tsx rename to x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_utility_bar.tsx index 4809a44528e2a..7f9b0dba04881 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/utility_bar.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_utility_bar.tsx @@ -25,25 +25,31 @@ import { } from '../../../../../common/components/utility_bar'; import * as i18n from '../translations'; import { useKibana } from '../../../../../common/lib/kibana'; -import { useRulesTableContextOptional } from './rules_table/rules_table_context'; +import { useRulesTableContext } from './rules_table/rules_table_context'; +import type { PaginationOptions } from '../../../../containers/detection_engine/rules/types'; -interface AllRulesUtilityBarProps { +export const getShowingRulesParams = ({ page, perPage, total: totalRules }: PaginationOptions) => { + const firstInPage = totalRules === 0 ? 0 : (page - 1) * perPage + 1; + const lastInPage = page * perPage > totalRules ? totalRules : page * perPage; + + return [firstInPage, lastInPage, totalRules] as const; +}; + +interface RulesTableUtilityBarProps { canBulkEdit: boolean; isAllSelected?: boolean; isAutoRefreshOn?: boolean; numberSelectedItems: number; onGetBulkItemsPopoverContent?: (closePopover: () => void) => EuiContextMenuPanelDescriptor[]; - onRefresh?: () => void; - onRefreshSwitch?: (checked: boolean) => void; - onToggleSelectAll?: () => void; - paginationTotal: number; - hasBulkActions: boolean; - hasPagination?: boolean; + onRefresh: () => void; + onRefreshSwitch: (checked: boolean) => void; + onToggleSelectAll: () => void; + pagination: PaginationOptions; isBulkActionInProgress?: boolean; hasDisabledActions?: boolean; } -export const AllRulesUtilityBar = React.memo( +export const RulesTableUtilityBar = React.memo( ({ canBulkEdit, isAllSelected, @@ -53,14 +59,12 @@ export const AllRulesUtilityBar = React.memo( onRefresh, onRefreshSwitch, onToggleSelectAll, - paginationTotal, - hasBulkActions = true, - hasPagination, + pagination, isBulkActionInProgress, hasDisabledActions, }) => { const { timelines } = useKibana().services; - const rulesTableContext = useRulesTableContextOptional(); + const rulesTableContext = useRulesTableContext(); const isAnyRuleSelected = numberSelectedItems > 0; const handleGetBulkItemsPopoverContent = useCallback( @@ -125,73 +129,44 @@ export const AllRulesUtilityBar = React.memo( - {hasBulkActions ? ( - - {i18n.SHOWING_RULES(paginationTotal)} - - ) : ( - - {i18n.SHOWING_EXCEPTION_LISTS(paginationTotal)} - - )} + + {i18n.SHOWING_RULES(...getShowingRulesParams(pagination))} + + <> + + + {i18n.SELECTED_RULES(numberSelectedItems)} + - {hasBulkActions ? ( - <> - - - {i18n.SELECTED_RULES(numberSelectedItems)} - - - {canBulkEdit && onToggleSelectAll && hasPagination && ( - - {isAllSelected ? i18n.CLEAR_SELECTION : i18n.SELECT_ALL_RULES(paginationTotal)} - - )} - - {canBulkEdit && ( - - {i18n.BATCH_ACTIONS} - - )} - + {canBulkEdit && ( - {i18n.REFRESH} + {isAllSelected ? i18n.CLEAR_SELECTION : i18n.SELECT_ALL_RULES(pagination.total)} + )} + + {canBulkEdit && ( - {i18n.REFRESH_RULE_POPOVER_LABEL} + {i18n.BATCH_ACTIONS} - - - ) : ( - + )} + ( > {i18n.REFRESH} + + {i18n.REFRESH_RULE_POPOVER_LABEL} + - )} + + + + {timelines.getLastUpdated({ + showUpdating: rulesTableContext.state.isFetching, + updatedAt: rulesTableContext.state.lastUpdated, + })} - {rulesTableContext && ( - - {timelines.getLastUpdated({ - showUpdating: rulesTableContext.state.isFetching, - updatedAt: rulesTableContext.state.lastUpdated, - })} - - )} ); } ); -AllRulesUtilityBar.displayName = 'AllRulesUtilityBar'; +RulesTableUtilityBar.displayName = 'RulesTableUtilityBar'; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx index 328b9a51b30a3..d04832d2e738c 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx @@ -5,8 +5,6 @@ * 2.0. */ -/* eslint-disable complexity */ - import { EuiBasicTable, EuiConfirmModal, @@ -33,7 +31,7 @@ import { showRulesTable } from './helpers'; import { useRulesTableContext } from './rules_table/rules_table_context'; import { useAsyncConfirmation } from './rules_table/use_async_confirmation'; import { RulesTableFilters } from './rules_table_filters/rules_table_filters'; -import { AllRulesUtilityBar } from './utility_bar'; +import { RulesTableUtilityBar } from './rules_table_utility_bar'; import { useBulkActionsDryRun } from './bulk_actions/use_bulk_actions_dry_run'; import { useBulkActionsConfirmation } from './bulk_actions/use_bulk_actions_confirmation'; import { useBulkEditFormFlyout } from './bulk_actions/use_bulk_edit_form_flyout'; @@ -147,7 +145,6 @@ export const RulesTables = React.memo( } = useBulkEditFormFlyout(); const selectedItemsCount = isAllSelected ? pagination.total : selectedRuleIds.length; - const hasPagination = pagination.total > pagination.perPage; const { isBulkActionsDryRunLoading, executeBulkActionsDryRun } = useBulkActionsDryRun(); @@ -334,10 +331,9 @@ export const RulesTables = React.memo( )} {shouldShowRulesTable && ( <> - ( onToggleSelectAll={toggleSelectAll} isBulkActionInProgress={isBulkActionsDryRunLoading || loadingRulesAction != null} hasDisabledActions={loadingRulesAction != null} - hasBulkActions /> +export const SHOWING_RULES = (firstInPage: number, lastOfPage: number, totalRules: number) => i18n.translate('xpack.securitySolution.detectionEngine.rules.allRules.showingRulesTitle', { - values: { totalRules }, - defaultMessage: 'Showing {totalRules} {totalRules, plural, =1 {rule} other {rules}}', + values: { firstInPage, lastOfPage, totalRules }, + defaultMessage: + 'Showing {firstInPage}-{lastOfPage} of {totalRules} {totalRules, plural, =1 {rule} other {rules}}', }); export const SELECT_ALL_RULES = (totalRules: number) => @@ -839,12 +840,6 @@ export const REFRESH_RULE_POPOVER_LABEL = i18n.translate( } ); -export const SHOWING_EXCEPTION_LISTS = (totalLists: number) => - i18n.translate('xpack.securitySolution.detectionEngine.rules.allRules.showingExceptionLists', { - values: { totalLists }, - defaultMessage: 'Showing {totalLists} {totalLists, plural, =1 {list} other {lists}}', - }); - /** * Bulk Export */ diff --git a/x-pack/plugins/security_solution/public/hosts/containers/hosts/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/hosts/index.tsx index e6e02c335180c..156af86d9df37 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/hosts/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/hosts/index.tsx @@ -17,7 +17,6 @@ import { generateTablePaginationOptions } from '../../../common/components/pagin import type { HostsEdges, PageInfoPaginated, - DocValueFields, HostsRequestOptions, } from '../../../../common/search_strategy'; import { HostsQueries } from '../../../../common/search_strategy'; @@ -44,7 +43,6 @@ export interface HostsArgs { } interface UseAllHost { - docValueFields?: DocValueFields[]; endDate: string; filterQuery?: ESTermQuery | string; indexNames: string[]; @@ -54,7 +52,6 @@ interface UseAllHost { } export const useAllHost = ({ - docValueFields, endDate, filterQuery, indexNames, @@ -136,7 +133,6 @@ export const useAllHost = ({ const myRequest = { ...(prevRequest ?? {}), defaultIndex: indexNames, - docValueFields: docValueFields ?? [], factoryQueryType: HostsQueries.hosts, filterQuery: createFilter(filterQuery), pagination: generateTablePaginationOptions(activePage, limit), @@ -155,17 +151,7 @@ export const useAllHost = ({ } return prevRequest; }); - }, [ - activePage, - direction, - docValueFields, - endDate, - filterQuery, - indexNames, - limit, - startDate, - sortField, - ]); + }, [activePage, direction, endDate, filterQuery, indexNames, limit, startDate, sortField]); useEffect(() => { if (!skip && hostsRequest) { diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.test.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.test.tsx index 7a95a328606cb..ee3ac4a907ee4 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.test.tsx @@ -146,7 +146,9 @@ describe('When the flyout is opened in the ArtifactListPage component', () => { let getByTestId: typeof renderResult['getByTestId']; beforeEach(async () => { - await render(); + await act(async () => { + await render(); + }); getByTestId = renderResult.getByTestId; @@ -166,34 +168,38 @@ describe('When the flyout is opened in the ArtifactListPage component', () => { } }); - it('should disable all buttons while an update is in flight', () => { - expect(getByTestId('testPage-flyout-cancelButton')).not.toBeEnabled(); - expect(getByTestId('testPage-flyout-submitButton')).not.toBeEnabled(); + it('should disable all buttons while an update is in flight', async () => { + await waitFor(() => { + expect(getByTestId('testPage-flyout-cancelButton')).not.toBeEnabled(); + expect(getByTestId('testPage-flyout-submitButton')).not.toBeEnabled(); + }); }); - it('should display loading indicator on Submit while an update is in flight', () => { - expect( - getByTestId('testPage-flyout-submitButton').querySelector('.euiLoadingSpinner') - ).toBeTruthy(); + it('should display loading indicator on Submit while an update is in flight', async () => { + await waitFor(() => + expect( + getByTestId('testPage-flyout-submitButton').querySelector('.euiLoadingSpinner') + ).toBeTruthy() + ); }); - it('should pass `disabled=true` to the Form component while an update is in flight', () => { - expect(getLastFormComponentProps().disabled).toBe(true); + it('should pass `disabled=true` to the Form component while an update is in flight', async () => { + await waitFor(() => expect(getLastFormComponentProps().disabled).toBe(true)); }); }); describe('and submit is successful', () => { beforeEach(async () => { - await render(); + await act(async () => { + await render(); + }); act(() => { userEvent.click(renderResult.getByTestId('testPage-flyout-submitButton')); }); - await act(async () => { - await waitFor(() => { - expect(renderResult.queryByTestId('testPage-flyout')).toBeNull(); - }); + await waitFor(() => { + expect(renderResult.queryByTestId('testPage-flyout')).toBeNull(); }); }); @@ -209,25 +215,25 @@ describe('When the flyout is opened in the ArtifactListPage component', () => { }); describe('and submit fails', () => { - beforeEach(async () => { + beforeEach(() => { const _renderAndWaitForFlyout = render; render = async (...args) => { - mockedApi.responseProvider.trustedAppCreate.mockImplementation(() => { - throw new Error('oh oh. no good!'); - }); + mockedApi.responseProvider.trustedAppCreate.mockRejectedValue( + new Error('oh oh. no good!') as never + ); - await _renderAndWaitForFlyout(...args); + await act(async () => { + await _renderAndWaitForFlyout(...args); + }); act(() => { userEvent.click(renderResult.getByTestId('testPage-flyout-submitButton')); }); - await act(async () => { - await waitFor(() => - expect(mockedApi.responseProvider.trustedAppCreate).toHaveBeenCalled() - ); - }); + await waitFor(() => + expect(mockedApi.responseProvider.trustedAppCreate).toHaveBeenCalled() + ); return renderResult; }; @@ -270,7 +276,9 @@ describe('When the flyout is opened in the ArtifactListPage component', () => { return new ExceptionsListItemGenerator().generateTrustedApp(item); }); - await render({ onFormSubmit: handleSubmitCallback }); + await act(async () => { + await render({ onFormSubmit: handleSubmitCallback }); + }); act(() => { userEvent.click(renderResult.getByTestId('testPage-flyout-submitButton')); @@ -319,8 +327,10 @@ describe('When the flyout is opened in the ArtifactListPage component', () => { }); describe('and in Edit mode', () => { - beforeEach(async () => { - history.push('somepage?show=edit&itemId=123'); + beforeEach(() => { + act(() => { + history.push('somepage?show=edit&itemId=123'); + }); }); it('should show loader while initializing in edit mode', async () => { @@ -355,10 +365,8 @@ describe('When the flyout is opened in the ArtifactListPage component', () => { it('should provide Form component with the item for edit', async () => { const { getByTestId } = await render(); - await act(async () => { - await waitFor(() => { - expect(getByTestId('formMock')).toBeTruthy(); - }); + await waitFor(() => { + expect(getByTestId('formMock')).toBeTruthy(); }); const expectedProps = { @@ -373,37 +381,34 @@ describe('When the flyout is opened in the ArtifactListPage component', () => { expectedProps.entries ) as ExceptionListItemSchema['entries']; - expect(getLastFormComponentProps().item).toEqual(expectedProps); + await waitFor(() => { + expect(getLastFormComponentProps().item).toEqual(expectedProps); + }); }); it('should show error toast and close flyout if item for edit does not exist', async () => { - mockedApi.responseProvider.trustedApp.mockImplementation(() => { - throw new Error('does not exist'); - }); - - await render(); + mockedApi.responseProvider.trustedApp.mockRejectedValue(new Error('does not exist') as never); await act(async () => { - await waitFor(() => { - expect(mockedApi.responseProvider.trustedApp).toHaveBeenCalled(); - }); + await render(); }); - expect(coreStart.notifications.toasts.addWarning).toHaveBeenCalledWith( - 'Failed to retrieve item for edit. Reason: does not exist' - ); + await waitFor(() => { + expect(mockedApi.responseProvider.trustedApp).toHaveBeenCalled(); + + expect(coreStart.notifications.toasts.addWarning).toHaveBeenCalledWith( + 'Failed to retrieve item for edit. Reason: does not exist' + ); + }); }); it('should not show the expired license callout', async () => { const { queryByTestId, getByTestId } = await render(); - await act(async () => { - await waitFor(() => { - expect(getByTestId('formMock')).toBeTruthy(); - }); + await waitFor(() => { + expect(getByTestId('formMock')).toBeTruthy(); + expect(queryByTestId('testPage-flyout-expiredLicenseCallout')).not.toBeTruthy(); }); - - expect(queryByTestId('testPage-flyout-expiredLicenseCallout')).not.toBeTruthy(); }); it('should show expired license warning when unsupported features are being used (downgrade scenario)', async () => { @@ -425,13 +430,10 @@ describe('When the flyout is opened in the ArtifactListPage component', () => { const { getByTestId } = await render(); - await act(async () => { - await waitFor(() => { - expect(getByTestId('formMock')).toBeTruthy(); - }); + await waitFor(() => { + expect(getByTestId('formMock')).toBeTruthy(); + expect(getByTestId('testPage-flyout-expiredLicenseCallout')).toBeTruthy(); }); - - expect(getByTestId('testPage-flyout-expiredLicenseCallout')).toBeTruthy(); }); }); }); diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.tsx index e0ec473d22dad..4d9f53c73341e 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.tsx +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.tsx @@ -231,7 +231,7 @@ export const ArtifactFlyout = memo( }, [externalSubmitHandlerError, internalSubmitError, submitHandler]); const { - isLoading: isLoadingItemForEdit, + isRefetching: isLoadingItemForEdit, error, refetch: fetchItemForEdit, } = useGetArtifact(apiClient, urlParams.itemId ?? '', undefined, { diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_with_artifact_list_data.ts b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_with_artifact_list_data.ts index 1f343142ee18e..813e205b64c9a 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_with_artifact_list_data.ts +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_with_artifact_list_data.ts @@ -7,7 +7,7 @@ import { useEffect, useMemo, useState } from 'react'; import type { Pagination } from '@elastic/eui'; -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import type { ServerApiError } from '../../../../common/types'; import { useIsMounted } from '../../../hooks/use_is_mounted'; import { MANAGEMENT_PAGE_SIZE_OPTIONS } from '../../../common/constants'; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/action_log_button.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/action_log_button.tsx index bdfd987af131b..85a5f2655dd68 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/action_log_button.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/action_log_button.tsx @@ -9,7 +9,7 @@ import React, { memo, useCallback, useState } from 'react'; import { EuiButton, EuiFlyout, EuiFlyoutBody, EuiFlyoutHeader, EuiTitle } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import type { EndpointResponderExtensionComponentProps } from './types'; -import { ResponseActionsList } from '../endpoint_response_actions_list/response_actions_list'; +import { ResponseActionsLog } from '../endpoint_response_actions_list/response_actions_log'; import { UX_MESSAGES } from '../endpoint_response_actions_list/translations'; export const ActionLogButton = memo((props) => { @@ -46,7 +46,7 @@ export const ActionLogButton = memo((p - + )} diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/status_action.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/status_action.tsx index de8987f47ea55..b75a480765844 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/status_action.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/status_action.tsx @@ -46,7 +46,7 @@ export const EndpointStatusActionResult = memo< error: fetchedDetailsError, isFetching, isFetched, - } = useGetEndpointDetails(endpointId, { enabled: isPending, queryKey }); + } = useGetEndpointDetails(endpointId, { enabled: isPending, queryKey: [queryKey] }); const { data: fetchedPendingActionsSummary } = useGetEndpointPendingActionsSummary([endpointId], { enabled: isPending, diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/action_list_date_range_picker.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_date_range_picker.tsx similarity index 79% rename from x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/action_list_date_range_picker.tsx rename to x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_date_range_picker.tsx index cf453e2070705..015fd3a501621 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/action_list_date_range_picker.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_date_range_picker.tsx @@ -5,8 +5,8 @@ * 2.0. */ -import React, { useCallback, memo, useState } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiSuperDatePicker, EuiSuperUpdateButton } from '@elastic/eui'; +import React, { memo, useState } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiSuperDatePicker } from '@elastic/eui'; import type { IDataPluginServices } from '@kbn/data-plugin/public'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import type { EuiSuperDatePickerRecentRange } from '@elastic/eui'; @@ -16,7 +16,7 @@ import type { OnRefreshChangeProps, } from '@elastic/eui/src/components/date_picker/types'; import { UI_SETTINGS } from '@kbn/data-plugin/common'; -import type { useGetEndpointActionList } from '../../../hooks'; + import { useTestIdGenerator } from '../../../hooks/use_test_id_generator'; export interface DateRangePickerValues { @@ -33,21 +33,19 @@ const DatePickerWrapper = euiStyled.div` padding-bottom: ${(props) => `${props.theme.eui.euiCodeBlockPaddingModifiers.paddingLarge}`}; `; -export const ActionListDateRangePicker = memo( +export const ActionLogDateRangePicker = memo( ({ dateRangePickerState, isDataLoading, onRefresh, onRefreshChange, onTimeChange, - onClick, }: { dateRangePickerState: DateRangePickerValues; isDataLoading: boolean; onRefresh: () => void; onRefreshChange: (evt: OnRefreshChangeProps) => void; onTimeChange: ({ start, end }: DurationRange) => void; - onClick: ReturnType['refetch']; }) => { const getTestId = useTestIdGenerator('response-actions-list'); const kibana = useKibana(); @@ -65,7 +63,6 @@ export const ActionListDateRangePicker = memo( }) ?? [] ); }); - const onClickCallback = useCallback(() => onClick(), [onClick]); return ( @@ -88,19 +85,10 @@ export const ActionListDateRangePicker = memo( width="auto" /> - - - ); } ); -ActionListDateRangePicker.displayName = 'ActionListDateRangePicker'; +ActionLogDateRangePicker.displayName = 'ActionLogDateRangePicker'; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_filter.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_filter.tsx new file mode 100644 index 0000000000000..1f89233a24ce7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_filter.tsx @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo, useCallback } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiSelectable, EuiPopoverTitle } from '@elastic/eui'; +import { ActionsLogFilterPopover } from './actions_log_filter_popover'; +import { type FilterItems, type FilterName, useActionsLogFilter } from './hooks'; +import { ClearAllButton } from './clear_all_button'; +import { UX_MESSAGES } from '../translations'; +import { useTestIdGenerator } from '../../../hooks/use_test_id_generator'; + +export const ActionsLogFilter = memo( + ({ + filterName, + onChangeCommandsFilter, + }: { + filterName: FilterName; + onChangeCommandsFilter: (selectedCommands: string[]) => void; + }) => { + const getTestId = useTestIdGenerator('response-actions-list'); + const { items, setItems, hasActiveFilters, numActiveFilters, numFilters } = + useActionsLogFilter(); + + const onChange = useCallback( + (newOptions: FilterItems) => { + setItems(newOptions.map((e) => e)); + + // update selected filter state + const selectedItems = newOptions.reduce((acc, curr) => { + if (curr.checked === 'on') { + acc.push(curr.key); + } + return acc; + }, []); + + // update query state + onChangeCommandsFilter(selectedItems); + }, + [onChangeCommandsFilter, setItems] + ); + + // clear all selected options + const onClearAll = useCallback(() => { + setItems( + items.map((e) => { + e.checked = undefined; + return e; + }) + ); + onChangeCommandsFilter([]); + }, [items, setItems, onChangeCommandsFilter]); + + return ( + + + {(list, search) => ( +
+ + {search} + + {list} + + + + + +
+ )} +
+
+ ); + } +); + +ActionsLogFilter.displayName = 'ActionsLogFilter'; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_filter_popover.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_filter_popover.tsx new file mode 100644 index 0000000000000..9d2aada871707 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_filter_popover.tsx @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo, useState, useCallback, useMemo } from 'react'; +import { EuiPopover, EuiFilterButton, useGeneratedHtmlId } from '@elastic/eui'; +import { FILTER_NAMES } from '../translations'; +import type { FilterName } from './hooks'; +import { useTestIdGenerator } from '../../../hooks/use_test_id_generator'; + +export const ActionsLogFilterPopover = memo( + ({ + children, + filterName, + hasActiveFilters, + numActiveFilters, + numFilters, + }: { + children: React.ReactNode; + filterName: FilterName; + hasActiveFilters: boolean; + numActiveFilters: number; + numFilters: number; + }) => { + const getTestId = useTestIdGenerator('response-actions-list'); + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const onButtonClick = useCallback(() => { + setIsPopoverOpen(!isPopoverOpen); + }, [setIsPopoverOpen, isPopoverOpen]); + const closePopover = useCallback(() => { + setIsPopoverOpen(false); + }, [setIsPopoverOpen]); + const filterGroupPopoverId = useGeneratedHtmlId({ + prefix: 'filterGroupPopover', + }); + + const button = useMemo( + () => ( + + {FILTER_NAMES[filterName]} + + ), + [ + filterName, + getTestId, + hasActiveFilters, + isPopoverOpen, + numActiveFilters, + numFilters, + onButtonClick, + ] + ); + + return ( + + {children} + + ); + } +); + +ActionsLogFilterPopover.displayName = 'ActionsLogFilterPopover'; 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 new file mode 100644 index 0000000000000..3f28d552276a2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/actions_log_filters.tsx @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { memo, useCallback, useMemo } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiFilterGroup, EuiSuperUpdateButton } from '@elastic/eui'; +import type { + DurationRange, + OnRefreshChangeProps, +} from '@elastic/eui/src/components/date_picker/types'; +import type { useGetEndpointActionList } from '../../../hooks'; +import { + type DateRangePickerValues, + ActionLogDateRangePicker, +} from './actions_log_date_range_picker'; +import { ActionsLogFilter } from './actions_log_filter'; +import type { FilterName } from './hooks'; +import { useTestIdGenerator } from '../../../hooks/use_test_id_generator'; + +export const ActionsLogFilters = memo( + ({ + dateRangePickerState, + isDataLoading, + onClick, + onChangeCommandsFilter, + onRefresh, + onRefreshChange, + onTimeChange, + }: { + dateRangePickerState: DateRangePickerValues; + isDataLoading: boolean; + onChangeCommandsFilter: (selectedCommands: string[]) => void; + onRefresh: () => void; + onRefreshChange: (evt: OnRefreshChangeProps) => void; + onTimeChange: ({ start, end }: DurationRange) => void; + onClick: ReturnType['refetch']; + }) => { + const getTestId = useTestIdGenerator('response-actions-list'); + const filters = useMemo(() => { + // TODO: add more filter names here (users, hosts, statuses) + const filterNames: FilterName[] = ['actions']; + return filterNames.map((filterName) => ( + + )); + }, [onChangeCommandsFilter]); + + const onClickRefreshButton = useCallback(() => onClick(), [onClick]); + + return ( + + + + + + {filters} + + + + + + ); + } +); + +ActionsLogFilters.displayName = 'ActionsLogFilters'; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/clear_all_button.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/clear_all_button.tsx new file mode 100644 index 0000000000000..96fd1dcc757aa --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/clear_all_button.tsx @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo } from 'react'; +import { euiStyled } from '@kbn/kibana-react-plugin/common'; +import { EuiButtonEmpty } from '@elastic/eui'; +import { UX_MESSAGES } from '../translations'; + +const StyledEuiButtonEmpty = euiStyled(EuiButtonEmpty).attrs({ + iconType: 'crossInACircleFilled', + color: 'danger', +})` + border-top: ${(props) => `${props.theme.eui.euiBorderThin}`}; + border-radius : 0; +`; +export const ClearAllButton = memo( + ({ + 'data-test-subj': dataTestSubj, + isDisabled, + onClick, + }: { + 'data-test-subj'?: string; + isDisabled: boolean; + onClick: () => void; + }) => { + return ( + + {UX_MESSAGES.filterClearAll} + + ); + } +); + +ClearAllButton.displayName = 'ClearAllButton'; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx index 5ea37b444dad1..304e77c655751 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/components/hooks.tsx @@ -5,12 +5,14 @@ * 2.0. */ -import { useCallback, useState } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import type { DurationRange, OnRefreshChangeProps, } from '@elastic/eui/src/components/date_picker/types'; -import type { DateRangePickerValues } from './action_list_date_range_picker'; +import type { DateRangePickerValues } from './actions_log_date_range_picker'; +import { RESPONSE_ACTION_COMMANDS } from '../../../../../common/endpoint/types'; +import type { FILTER_NAMES } from '../translations'; const defaultDateRangeOptions = Object.freeze({ autoRefreshOptions: { @@ -85,3 +87,30 @@ export const useDateRangePicker = () => { return { dateRangePickerState, onRefreshChange, onTimeChange }; }; + +export type FilterItems = Array<{ + key: string; + label: string; + checked: 'on' | undefined; +}>; + +// TODO: add more filter names here +export type FilterName = keyof typeof FILTER_NAMES; +export const useActionsLogFilter = () => { + const [items, setItems] = useState( + RESPONSE_ACTION_COMMANDS.slice().map((filter) => ({ + key: filter, + label: filter === 'unisolate' ? 'release' : filter, + checked: undefined, + })) + ); + + const hasActiveFilters = useMemo(() => !!items.find((item) => item.checked === 'on'), [items]); + const numActiveFilters = useMemo( + () => items.filter((item) => item.checked === 'on').length, + [items] + ); + const numFilters = useMemo(() => items.filter((item) => item.checked !== 'on').length, [items]); + + return { items, setItems, hasActiveFilters, numActiveFilters, numFilters }; +}; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_list.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_list.tsx deleted file mode 100644 index 169128a373976..0000000000000 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_list.tsx +++ /dev/null @@ -1,547 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - EuiAvatar, - EuiBadge, - EuiBasicTable, - EuiButtonIcon, - EuiDescriptionList, - EuiEmptyPrompt, - EuiFacetButton, - EuiFlexGroup, - EuiFlexItem, - EuiHorizontalRule, - EuiScreenReaderOnly, - EuiI18nNumber, - EuiText, - EuiCodeBlock, - EuiToolTip, - RIGHT_ALIGNMENT, -} from '@elastic/eui'; -import { euiStyled, css } from '@kbn/kibana-react-plugin/common'; - -import type { HorizontalAlignment, CriteriaWithPagination } from '@elastic/eui'; -import React, { memo, useCallback, useMemo, useState } from 'react'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { getEmptyValue } from '../../../common/components/empty_value'; -import { FormattedDate } from '../../../common/components/formatted_date'; -import type { ActionDetails } from '../../../../common/endpoint/types'; -import type { EndpointActionListRequestQuery } from '../../../../common/endpoint/schema/actions'; -import { ManagementEmptyStateWrapper } from '../management_empty_state_wrapper'; -import { useGetEndpointActionList } from '../../hooks'; -import { OUTPUT_MESSAGES, TABLE_COLUMN_NAMES, UX_MESSAGES } from './translations'; -import { MANAGEMENT_PAGE_SIZE_OPTIONS } from '../../common/constants'; -import { useTestIdGenerator } from '../../hooks/use_test_id_generator'; -import { ActionListDateRangePicker } from './components/action_list_date_range_picker'; -import { useDateRangePicker } from './components/hooks'; - -const emptyValue = getEmptyValue(); - -const getCommand = ( - command: ActionDetails['command'] -): Exclude | 'release' => - command === 'unisolate' ? 'release' : command; - -// Truncated usernames -const StyledFacetButton = euiStyled(EuiFacetButton)` - .euiText { - margin-top: 0.38rem; - overflow-y: visible !important; - } -`; - -const customDescriptionListCss = css` - &.euiDescriptionList { - > .euiDescriptionList__title { - color: ${(props) => props.theme.eui.euiColorDarkShade}; - font-size: ${(props) => props.theme.eui.euiFontSizeXS}; - margin-top: ${(props) => props.theme.eui.euiSizeS}; - } - - > .euiDescriptionList__description { - font-weight: ${(props) => props.theme.eui.euiFontWeightSemiBold}; - margin-top: ${(props) => props.theme.eui.euiSizeS}; - } - } -`; - -const StyledDescriptionList = euiStyled(EuiDescriptionList).attrs({ - compressed: true, - type: 'column', -})` - ${customDescriptionListCss} -`; - -// output section styles -const topSpacingCss = css` - ${(props) => `${props.theme.eui.euiCodeBlockPaddingModifiers.paddingMedium} 0`} -`; -const dashedBorderCss = css` - ${(props) => `1px dashed ${props.theme.eui.euiColorDisabled}`}; -`; -const StyledDescriptionListOutput = euiStyled(EuiDescriptionList).attrs({ compressed: true })` - ${customDescriptionListCss} - dd { - margin: ${topSpacingCss}; - padding: ${topSpacingCss}; - border-top: ${dashedBorderCss}; - border-bottom: ${dashedBorderCss}; - } -`; - -// code block styles -const StyledEuiCodeBlock = euiStyled(EuiCodeBlock).attrs({ - transparentBackground: true, - paddingSize: 'none', -})` - code { - color: ${(props) => props.theme.eui.euiColorDarkShade} !important; - } -`; - -export const ResponseActionsList = memo< - Pick ->(({ agentIds, commands, userIds }) => { - const getTestId = useTestIdGenerator('response-actions-list'); - const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState<{ - [k: ActionDetails['id']]: React.ReactNode; - }>({}); - - const [queryParams, setQueryParams] = useState({ - page: 1, - pageSize: 10, - agentIds, - commands, - userIds, - }); - - // date range picker state and handlers - const { dateRangePickerState, onRefreshChange, onTimeChange } = useDateRangePicker(); - - // initial fetch of list data - const { - error, - data: actionList, - isFetching, - isFetched, - refetch: reFetchEndpointActionList, - } = useGetEndpointActionList({ - ...queryParams, - startDate: dateRangePickerState.startDate, - endDate: dateRangePickerState.endDate, - }); - - // handle auto refresh data - const onRefresh = useCallback(() => { - if (dateRangePickerState.autoRefreshOptions.enabled) { - reFetchEndpointActionList(); - } - }, [dateRangePickerState.autoRefreshOptions.enabled, reFetchEndpointActionList]); - - // total actions - const totalItemCount = useMemo(() => actionList?.total ?? 0, [actionList]); - - // expanded tray contents - const toggleDetails = useCallback( - (item: ActionDetails) => { - const itemIdToExpandedRowMapValues = { ...itemIdToExpandedRowMap }; - if (itemIdToExpandedRowMapValues[item.id]) { - delete itemIdToExpandedRowMapValues[item.id]; - } else { - const { - startedAt, - completedAt, - isCompleted, - wasSuccessful, - isExpired, - command: _command, - parameters, - } = item; - - const parametersList = parameters - ? Object.entries(parameters).map(([key, value]) => { - return `${key}:${value}`; - }) - : undefined; - - const command = getCommand(_command); - const dataList = [ - { - title: OUTPUT_MESSAGES.expandSection.placedAt, - description: `${startedAt}`, - }, - { - title: OUTPUT_MESSAGES.expandSection.startedAt, - description: `${startedAt}`, - }, - { - title: OUTPUT_MESSAGES.expandSection.completedAt, - description: `${completedAt ?? emptyValue}`, - }, - { - title: OUTPUT_MESSAGES.expandSection.input, - description: `${command}`, - }, - { - title: OUTPUT_MESSAGES.expandSection.parameters, - description: parametersList ? parametersList : emptyValue, - }, - ].map(({ title, description }) => { - return { - title: {title}, - description: {description}, - }; - }); - - const outputList = [ - { - title: ( - {`${OUTPUT_MESSAGES.expandSection.output}:`} - ), - description: ( - // codeblock for output - - {isExpired - ? OUTPUT_MESSAGES.hasExpired(command) - : isCompleted - ? wasSuccessful - ? OUTPUT_MESSAGES.wasSuccessful(command) - : OUTPUT_MESSAGES.hasFailed(command) - : OUTPUT_MESSAGES.isPending(command)} - - ), - }, - ]; - - itemIdToExpandedRowMapValues[item.id] = ( - <> - - - - - - - - - - ); - } - setItemIdToExpandedRowMap(itemIdToExpandedRowMapValues); - }, - [getTestId, itemIdToExpandedRowMap] - ); - // memoized callback for toggleDetails - const onClickCallback = useCallback( - (data: ActionDetails) => () => toggleDetails(data), - [toggleDetails] - ); - - // table column - const responseActionListColumns = useMemo(() => { - const hideHostColumn = typeof agentIds === 'string'; - - const columns = [ - { - field: 'startedAt', - name: TABLE_COLUMN_NAMES.time, - width: hideHostColumn ? '21%' : '15%', - truncateText: true, - render: (startedAt: ActionDetails['startedAt']) => { - return ( - - ); - }, - }, - { - field: 'command', - name: TABLE_COLUMN_NAMES.command, - width: hideHostColumn ? '21%' : '10%', - truncateText: true, - render: (_command: ActionDetails['command']) => { - const command = getCommand(_command); - return ( - - - - ); - }, - }, - { - field: 'createdBy', - name: TABLE_COLUMN_NAMES.user, - width: hideHostColumn ? '21%' : '14%', - truncateText: true, - render: (userId: ActionDetails['createdBy']) => { - return ( - - } - > - - - {userId} - - - - ); - }, - }, - // conditional hostname column - { - field: 'agents', - name: TABLE_COLUMN_NAMES.host, - width: '20%', - truncateText: true, - render: (agents: ActionDetails['agents']) => { - // TODO: compute host names later with hostMetadata? (using agent Ids for now) - const hostname = agents?.[0] ?? ''; - return ( - - - {hostname} - - - ); - }, - }, - { - field: 'comment', - name: TABLE_COLUMN_NAMES.comments, - width: hideHostColumn ? '21%' : '30%', - truncateText: true, - render: (comment: ActionDetails['comment']) => { - return ( - - - {comment ?? emptyValue} - - - ); - }, - }, - { - field: 'isCompleted', - name: TABLE_COLUMN_NAMES.status, - width: hideHostColumn ? '15%' : '10%', - render: (isCompleted: ActionDetails['isCompleted'], data: ActionDetails) => { - const status = data.isExpired - ? UX_MESSAGES.badge.failed - : isCompleted - ? data.wasSuccessful - ? UX_MESSAGES.badge.completed - : UX_MESSAGES.badge.failed - : UX_MESSAGES.badge.pending; - - return ( - - - - - - ); - }, - }, - { - field: '', - align: RIGHT_ALIGNMENT as HorizontalAlignment, - width: '40px', - isExpander: true, - name: ( - - {UX_MESSAGES.screenReaderExpand} - - ), - render: (data: ActionDetails) => { - return ( - - ); - }, - }, - ]; - // filter out the host column - if (hideHostColumn) { - return columns.filter((column) => column.field !== 'agents'); - } - return columns; - }, [agentIds, getTestId, itemIdToExpandedRowMap, onClickCallback]); - - // table pagination - const tablePagination = useMemo(() => { - return { - // this controls the table UI page - // to match 0-based table paging - pageIndex: (queryParams.page || 1) - 1, - pageSize: queryParams.pageSize || 10, - totalItemCount, - pageSizeOptions: MANAGEMENT_PAGE_SIZE_OPTIONS as number[], - }; - }, [queryParams, totalItemCount]); - - // handle onChange - const handleTableOnChange = useCallback( - ({ page: _page }: CriteriaWithPagination) => { - // table paging is 0 based - const { index, size } = _page; - setQueryParams((prevState) => ({ - ...prevState, - // adjust the page to conform to - // 1-based API page - page: index + 1, - pageSize: size, - })); - reFetchEndpointActionList(); - }, - [reFetchEndpointActionList, setQueryParams] - ); - - // compute record ranges - const pagedResultsCount = useMemo(() => { - const page = queryParams.page ?? 1; - const perPage = queryParams?.pageSize ?? 10; - - const totalPages = Math.ceil(totalItemCount / perPage); - const fromCount = perPage * page - perPage + 1; - const toCount = - page === totalPages || totalPages === 1 ? totalItemCount : fromCount + perPage - 1; - return { fromCount, toCount }; - }, [queryParams.page, queryParams.pageSize, totalItemCount]); - - // create range label to display - const recordRangeLabel = useMemo( - () => ( - - - - {'-'} - - - ), - total: , - recordsLabel: {UX_MESSAGES.recordsLabel(totalItemCount)}, - }} - /> - - ), - [getTestId, pagedResultsCount.fromCount, pagedResultsCount.toCount, totalItemCount] - ); - - return ( - <> - - {isFetched && !totalItemCount ? ( - - - - - - } - body={ -

- -

- } - data-test-subj="responseActions-empty" - /> -
-
- ) : ( - <> - {recordRangeLabel} - - - - )} - - ); -}); - -ResponseActionsList.displayName = 'ResponseActionsList'; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_list.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx similarity index 88% rename from x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_list.test.tsx rename to x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx index 57cbfff15790b..a731bf784f2e9 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_list.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.test.tsx @@ -12,7 +12,7 @@ import userEvent from '@testing-library/user-event'; import { waitForEuiPopoverOpen } from '@elastic/eui/lib/test/rtl'; import type { AppContextTestRender } from '../../../common/mock/endpoint'; import { createAppRootMockRenderer } from '../../../common/mock/endpoint'; -import { ResponseActionsList } from './response_actions_list'; +import { ResponseActionsLog } from './response_actions_log'; import type { ActionDetails, ActionListApiResponse } from '../../../../common/endpoint/types'; import { MANAGEMENT_PATH } from '../../../../common/constants'; import { EndpointActionGenerator } from '../../../../common/endpoint/data_generators/endpoint_action_generator'; @@ -106,11 +106,11 @@ jest.mock('@kbn/kibana-react-plugin/public', () => { }; }); -describe('Response Actions List', () => { +describe('Response Actions Log', () => { const testPrefix = 'response-actions-list'; let render: ( - props?: React.ComponentProps + props?: React.ComponentProps ) => ReturnType; let renderResult: ReturnType; let history: AppContextTestRender['history']; @@ -127,8 +127,8 @@ describe('Response Actions List', () => { beforeEach(async () => { mockedContext = createAppRootMockRenderer(); ({ history } = mockedContext); - render = (props?: React.ComponentProps) => - (renderResult = mockedContext.render()); + render = (props?: React.ComponentProps) => + (renderResult = mockedContext.render()); reactTestingLibrary.act(() => { history.push(`${MANAGEMENT_PATH}/response_actions`); }); @@ -152,6 +152,11 @@ describe('Response Actions List', () => { expect(renderResult.getByTestId(`${testPrefix}-super-date-picker`)).toBeTruthy(); }); + it('should show actions filter', () => { + render(); + expect(renderResult.getByTestId(`${testPrefix}-actions-filter-popoverButton`)).toBeTruthy(); + }); + it('should show empty state when there is no data', async () => { mockUseGetEndpointActionList = { ...baseMockedActionList, @@ -282,9 +287,7 @@ describe('Response Actions List', () => { it('should refresh data when super date picker refresh button is clicked', async () => { render(); - const superRefreshButton = renderResult.getByTestId( - `${testPrefix}-super-date-picker-refresh-button` - ); + const superRefreshButton = renderResult.getByTestId(`${testPrefix}-super-refresh-button`); userEvent.click(superRefreshButton); expect(refetchFunction).toHaveBeenCalledTimes(1); }); @@ -414,6 +417,40 @@ describe('Response Actions List', () => { ).toEqual(['Time', 'Command', 'User', 'Host', 'Comments', 'Status']); }); }); + + describe('Actions filter', () => { + const filterPrefix = '-actions-filter'; + + it('should have a search bar', () => { + render(); + userEvent.click(renderResult.getByTestId(`${testPrefix}${filterPrefix}-popoverButton`)); + const searchBar = renderResult.getByTestId(`${testPrefix}${filterPrefix}-search`); + expect(searchBar).toBeTruthy(); + expect(searchBar.querySelector('input')?.getAttribute('placeholder')).toEqual( + 'Search actions' + ); + }); + + it('should show a list of actions when opened', () => { + render(); + userEvent.click(renderResult.getByTestId(`${testPrefix}${filterPrefix}-popoverButton`)); + const filterList = renderResult.getByTestId(`${testPrefix}${filterPrefix}-popoverList`); + expect(filterList).toBeTruthy(); + expect(filterList.querySelectorAll('ul>li').length).toEqual(5); + expect( + Array.from(filterList.querySelectorAll('ul>li')).map((option) => option.textContent) + ).toEqual(['isolate', 'release', 'kill-process', 'suspend-process', 'running-processes']); + }); + + it('should have `clear all` button `disabled` when no selected values', () => { + render(); + userEvent.click(renderResult.getByTestId(`${testPrefix}${filterPrefix}-popoverButton`)); + const clearAllButton = renderResult.getByTestId( + `${testPrefix}${filterPrefix}-clearAllButton` + ); + expect(clearAllButton.hasAttribute('disabled')).toBeTruthy(); + }); + }); }); // mock API response 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 new file mode 100644 index 0000000000000..7afb5ce81f4cf --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_response_actions_list/response_actions_log.tsx @@ -0,0 +1,556 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + EuiAvatar, + EuiBadge, + EuiBasicTable, + EuiButtonIcon, + EuiDescriptionList, + EuiEmptyPrompt, + EuiFacetButton, + EuiFlexGroup, + EuiFlexItem, + EuiHorizontalRule, + EuiScreenReaderOnly, + EuiI18nNumber, + EuiText, + EuiCodeBlock, + EuiToolTip, + RIGHT_ALIGNMENT, +} from '@elastic/eui'; +import { euiStyled, css } from '@kbn/kibana-react-plugin/common'; + +import type { HorizontalAlignment, CriteriaWithPagination } from '@elastic/eui'; +import React, { memo, useCallback, useMemo, useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { getEmptyValue } from '../../../common/components/empty_value'; +import { FormattedDate } from '../../../common/components/formatted_date'; +import type { ActionDetails } from '../../../../common/endpoint/types'; +import type { EndpointActionListRequestQuery } from '../../../../common/endpoint/schema/actions'; +import { ManagementEmptyStateWrapper } from '../management_empty_state_wrapper'; +import { useGetEndpointActionList } from '../../hooks'; +import { OUTPUT_MESSAGES, TABLE_COLUMN_NAMES, UX_MESSAGES } from './translations'; +import { MANAGEMENT_PAGE_SIZE_OPTIONS } from '../../common/constants'; +import { useTestIdGenerator } from '../../hooks/use_test_id_generator'; +import { ActionsLogFilters } from './components/actions_log_filters'; +import { useDateRangePicker } from './components/hooks'; + +const emptyValue = getEmptyValue(); + +const getCommand = ( + command: ActionDetails['command'] +): Exclude | 'release' => + command === 'unisolate' ? 'release' : command; + +// Truncated usernames +const StyledFacetButton = euiStyled(EuiFacetButton)` + .euiText { + margin-top: 0.38rem; + overflow-y: visible !important; + } +`; + +const customDescriptionListCss = css` + &.euiDescriptionList { + > .euiDescriptionList__title { + color: ${(props) => props.theme.eui.euiColorDarkShade}; + font-size: ${(props) => props.theme.eui.euiFontSizeXS}; + margin-top: ${(props) => props.theme.eui.euiSizeS}; + } + + > .euiDescriptionList__description { + font-weight: ${(props) => props.theme.eui.euiFontWeightSemiBold}; + margin-top: ${(props) => props.theme.eui.euiSizeS}; + } + } +`; + +const StyledDescriptionList = euiStyled(EuiDescriptionList).attrs({ + compressed: true, + type: 'column', +})` + ${customDescriptionListCss} +`; + +// output section styles +const topSpacingCss = css` + ${(props) => `${props.theme.eui.euiCodeBlockPaddingModifiers.paddingMedium} 0`} +`; +const dashedBorderCss = css` + ${(props) => `1px dashed ${props.theme.eui.euiColorDisabled}`}; +`; +const StyledDescriptionListOutput = euiStyled(EuiDescriptionList).attrs({ compressed: true })` + ${customDescriptionListCss} + dd { + margin: ${topSpacingCss}; + padding: ${topSpacingCss}; + border-top: ${dashedBorderCss}; + border-bottom: ${dashedBorderCss}; + } +`; + +// code block styles +const StyledEuiCodeBlock = euiStyled(EuiCodeBlock).attrs({ + transparentBackground: true, + paddingSize: 'none', +})` + code { + color: ${(props) => props.theme.eui.euiColorDarkShade} !important; + } +`; + +export const ResponseActionsLog = memo>( + ({ agentIds }) => { + const getTestId = useTestIdGenerator('response-actions-list'); + const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState<{ + [k: ActionDetails['id']]: React.ReactNode; + }>({}); + + const [queryParams, setQueryParams] = useState({ + page: 1, + pageSize: 10, + agentIds, + commands: [], + userIds: [], + }); + + // date range picker state and handlers + const { dateRangePickerState, onRefreshChange, onTimeChange } = useDateRangePicker(); + + // initial fetch of list data + const { + error, + data: actionList, + isFetching, + isFetched, + refetch: reFetchEndpointActionList, + } = useGetEndpointActionList({ + ...queryParams, + startDate: dateRangePickerState.startDate, + endDate: dateRangePickerState.endDate, + }); + + // handle auto refresh data + const onRefresh = useCallback(() => { + if (dateRangePickerState.autoRefreshOptions.enabled) { + reFetchEndpointActionList(); + } + }, [dateRangePickerState.autoRefreshOptions.enabled, reFetchEndpointActionList]); + + // handle on change actions filter + const onChangeCommandsFilter = useCallback( + (selectedCommands: string[]) => { + setQueryParams((prevState) => ({ ...prevState, commands: selectedCommands })); + }, + [setQueryParams] + ); + + // total actions + const totalItemCount = useMemo(() => actionList?.total ?? 0, [actionList]); + + // expanded tray contents + const toggleDetails = useCallback( + (item: ActionDetails) => { + const itemIdToExpandedRowMapValues = { ...itemIdToExpandedRowMap }; + if (itemIdToExpandedRowMapValues[item.id]) { + delete itemIdToExpandedRowMapValues[item.id]; + } else { + const { + startedAt, + completedAt, + isCompleted, + wasSuccessful, + isExpired, + command: _command, + parameters, + } = item; + + const parametersList = parameters + ? Object.entries(parameters).map(([key, value]) => { + return `${key}:${value}`; + }) + : undefined; + + const command = getCommand(_command); + const dataList = [ + { + title: OUTPUT_MESSAGES.expandSection.placedAt, + description: `${startedAt}`, + }, + { + title: OUTPUT_MESSAGES.expandSection.startedAt, + description: `${startedAt}`, + }, + { + title: OUTPUT_MESSAGES.expandSection.completedAt, + description: `${completedAt ?? emptyValue}`, + }, + { + title: OUTPUT_MESSAGES.expandSection.input, + description: `${command}`, + }, + { + title: OUTPUT_MESSAGES.expandSection.parameters, + description: parametersList ? parametersList : emptyValue, + }, + ].map(({ title, description }) => { + return { + title: {title}, + description: {description}, + }; + }); + + const outputList = [ + { + title: ( + {`${OUTPUT_MESSAGES.expandSection.output}:`} + ), + description: ( + // codeblock for output + + {isExpired + ? OUTPUT_MESSAGES.hasExpired(command) + : isCompleted + ? wasSuccessful + ? OUTPUT_MESSAGES.wasSuccessful(command) + : OUTPUT_MESSAGES.hasFailed(command) + : OUTPUT_MESSAGES.isPending(command)} + + ), + }, + ]; + + itemIdToExpandedRowMapValues[item.id] = ( + <> + + + + + + + + + + ); + } + setItemIdToExpandedRowMap(itemIdToExpandedRowMapValues); + }, + [getTestId, itemIdToExpandedRowMap] + ); + // memoized callback for toggleDetails + const onClickCallback = useCallback( + (data: ActionDetails) => () => toggleDetails(data), + [toggleDetails] + ); + + // table column + const responseActionListColumns = useMemo(() => { + const hideHostColumn = typeof agentIds === 'string'; + + const columns = [ + { + field: 'startedAt', + name: TABLE_COLUMN_NAMES.time, + width: hideHostColumn ? '21%' : '15%', + truncateText: true, + render: (startedAt: ActionDetails['startedAt']) => { + return ( + + ); + }, + }, + { + field: 'command', + name: TABLE_COLUMN_NAMES.command, + width: hideHostColumn ? '21%' : '10%', + truncateText: true, + render: (_command: ActionDetails['command']) => { + const command = getCommand(_command); + return ( + + + + ); + }, + }, + { + field: 'createdBy', + name: TABLE_COLUMN_NAMES.user, + width: hideHostColumn ? '21%' : '14%', + truncateText: true, + render: (userId: ActionDetails['createdBy']) => { + return ( + + } + > + + + {userId} + + + + ); + }, + }, + // conditional hostname column + { + field: 'agents', + name: TABLE_COLUMN_NAMES.host, + width: '20%', + truncateText: true, + render: (agents: ActionDetails['agents']) => { + // TODO: compute host names later with hostMetadata? (using agent Ids for now) + const hostname = agents?.[0] ?? ''; + return ( + + + {hostname} + + + ); + }, + }, + { + field: 'comment', + name: TABLE_COLUMN_NAMES.comments, + width: hideHostColumn ? '21%' : '30%', + truncateText: true, + render: (comment: ActionDetails['comment']) => { + return ( + + + {comment ?? emptyValue} + + + ); + }, + }, + { + field: 'isCompleted', + name: TABLE_COLUMN_NAMES.status, + width: hideHostColumn ? '15%' : '10%', + render: (isCompleted: ActionDetails['isCompleted'], data: ActionDetails) => { + const status = data.isExpired + ? UX_MESSAGES.badge.failed + : isCompleted + ? data.wasSuccessful + ? UX_MESSAGES.badge.completed + : UX_MESSAGES.badge.failed + : UX_MESSAGES.badge.pending; + + return ( + + + + + + ); + }, + }, + { + field: '', + align: RIGHT_ALIGNMENT as HorizontalAlignment, + width: '40px', + isExpander: true, + name: ( + + {UX_MESSAGES.screenReaderExpand} + + ), + render: (data: ActionDetails) => { + return ( + + ); + }, + }, + ]; + // filter out the host column + if (hideHostColumn) { + return columns.filter((column) => column.field !== 'agents'); + } + return columns; + }, [agentIds, getTestId, itemIdToExpandedRowMap, onClickCallback]); + + // table pagination + const tablePagination = useMemo(() => { + return { + // this controls the table UI page + // to match 0-based table paging + pageIndex: (queryParams.page || 1) - 1, + pageSize: queryParams.pageSize || 10, + totalItemCount, + pageSizeOptions: MANAGEMENT_PAGE_SIZE_OPTIONS as number[], + }; + }, [queryParams, totalItemCount]); + + // handle onChange + const handleTableOnChange = useCallback( + ({ page: _page }: CriteriaWithPagination) => { + // table paging is 0 based + const { index, size } = _page; + setQueryParams((prevState) => ({ + ...prevState, + // adjust the page to conform to + // 1-based API page + page: index + 1, + pageSize: size, + })); + reFetchEndpointActionList(); + }, + [reFetchEndpointActionList, setQueryParams] + ); + + // compute record ranges + const pagedResultsCount = useMemo(() => { + const page = queryParams.page ?? 1; + const perPage = queryParams?.pageSize ?? 10; + + const totalPages = Math.ceil(totalItemCount / perPage); + const fromCount = perPage * page - perPage + 1; + const toCount = + page === totalPages || totalPages === 1 ? totalItemCount : fromCount + perPage - 1; + return { fromCount, toCount }; + }, [queryParams.page, queryParams.pageSize, totalItemCount]); + + // create range label to display + const recordRangeLabel = useMemo( + () => ( + + + + {'-'} + + + ), + total: , + recordsLabel: {UX_MESSAGES.recordsLabel(totalItemCount)}, + }} + /> + + ), + [getTestId, pagedResultsCount.fromCount, pagedResultsCount.toCount, totalItemCount] + ); + + return ( + <> + + {isFetched && !totalItemCount ? ( + + + + + + } + body={ +

+ +

+ } + data-test-subj="responseActions-empty" + /> +
+
+ ) : ( + <> + {recordRangeLabel} + + + + )} + + ); + } +); + +ResponseActionsLog.displayName = 'ResponseActionsLog'; 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 04255a9ceaf53..fe3d368e8aec7 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 @@ -101,6 +101,17 @@ export const UX_MESSAGES = Object.freeze({ fetchError: i18n.translate('xpack.securitySolution.responseActionsList.list.errorMessage', { defaultMessage: 'Error while retrieving response actions', }), + filterClearAll: i18n.translate( + 'xpack.securitySolution.responseActionsList.list.filter.clearAll', + { + defaultMessage: 'Clear all', + } + ), + filterSearchPlaceholder: (filterName: string) => + i18n.translate('xpack.securitySolution.responseActionsList.list.filter.searchPlaceholder', { + defaultMessage: 'Search {filterName}', + values: { filterName }, + }), badge: { completed: i18n.translate( 'xpack.securitySolution.responseActionsList.list.item.badge.completed', @@ -129,3 +140,10 @@ export const UX_MESSAGES = Object.freeze({ }, }), }); + +// TODO: Add more filter names here (hosts, statuses) etc +export const FILTER_NAMES = Object.freeze({ + actions: i18n.translate('xpack.securitySolution.responseActionsList.list.filter.actions', { + defaultMessage: 'Actions', + }), +}); diff --git a/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response_wrapper.test.tsx b/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response_wrapper.test.tsx index 7bc3e5ac0be6d..3c6d4f66d59cb 100644 --- a/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response_wrapper.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response_wrapper.test.tsx @@ -25,18 +25,12 @@ import { policyResponseTitles, } from './policy_response_friendly_names'; +jest.setTimeout(10000); + jest.mock('../../hooks/endpoint/use_get_endpoint_policy_response'); jest.mock('../../hooks/endpoint/use_get_endpoint_details'); -// FLAKY: https://github.com/elastic/kibana/issues/136272 -// FLAKY: https://github.com/elastic/kibana/issues/139033 -// FLAKY: https://github.com/elastic/kibana/issues/139032 -// FLAKY: https://github.com/elastic/kibana/issues/139031 -// FLAKY: https://github.com/elastic/kibana/issues/139030 -// FLAKY: https://github.com/elastic/kibana/issues/139028 -// FLAKY: https://github.com/elastic/kibana/issues/139029 -// FLAKY: https://github.com/elastic/kibana/issues/139027 -describe.skip('when on the policy response', () => { +describe('when on the policy response', () => { const docGenerator = new EndpointDocGenerator(); const createPolicyResponse = ( overallStatus: HostPolicyResponseActionStatus = HostPolicyResponseActionStatus.success, diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_delete_artifact.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_delete_artifact.tsx index e75402ae98427..aef46affd4252 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_delete_artifact.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_delete_artifact.tsx @@ -7,8 +7,8 @@ import pMap from 'p-map'; import type { IHttpFetchError } from '@kbn/core-http-browser'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; -import type { UseMutationOptions, UseMutationResult } from 'react-query'; -import { useMutation } from 'react-query'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; +import { useMutation } from '@tanstack/react-query'; import type { ExceptionsListApiClient } from '../../services/exceptions_list/exceptions_list_api_client'; const DEFAULT_OPTIONS = Object.freeze({}); diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_update_artifact.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_update_artifact.tsx index fdbe6a451dc44..be99fb5f1abb8 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_update_artifact.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_update_artifact.tsx @@ -10,8 +10,8 @@ import type { UpdateExceptionListItemSchema, ExceptionListItemSchema, } from '@kbn/securitysolution-io-ts-list-types'; -import type { UseMutationOptions, UseMutationResult } from 'react-query'; -import { useMutation } from 'react-query'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; +import { useMutation } from '@tanstack/react-query'; import type { ExceptionsListApiClient } from '../../services/exceptions_list/exceptions_list_api_client'; const DEFAULT_OPTIONS = Object.freeze({}); diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_create_artifact.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_create_artifact.tsx index fa807e2d93438..63a41492b2699 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_create_artifact.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_create_artifact.tsx @@ -9,8 +9,8 @@ import type { ExceptionListItemSchema, } from '@kbn/securitysolution-io-ts-list-types'; import type { IHttpFetchError } from '@kbn/core-http-browser'; -import type { UseMutationOptions, UseMutationResult } from 'react-query'; -import { useMutation } from 'react-query'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; +import { useMutation } from '@tanstack/react-query'; import type { ExceptionsListApiClient } from '../../services/exceptions_list/exceptions_list_api_client'; const DEFAULT_OPTIONS = Object.freeze({}); diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_delete_artifact.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_delete_artifact.tsx index de3b4c386bc3f..9b366d033caf5 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_delete_artifact.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_delete_artifact.tsx @@ -6,8 +6,8 @@ */ import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import type { IHttpFetchError } from '@kbn/core-http-browser'; -import type { UseMutationOptions, UseMutationResult } from 'react-query'; -import { useMutation } from 'react-query'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; +import { useMutation } from '@tanstack/react-query'; import type { ExceptionsListApiClient } from '../../services/exceptions_list/exceptions_list_api_client'; const DEFAULT_OPTIONS = Object.freeze({}); diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_get_artifact.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_get_artifact.tsx index 7ce83d84c9394..58b0cd1c16e73 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_get_artifact.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_get_artifact.tsx @@ -6,8 +6,8 @@ */ import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import type { IHttpFetchError } from '@kbn/core-http-browser'; -import type { QueryObserverResult, UseQueryOptions } from 'react-query'; -import { useQuery } from 'react-query'; +import type { QueryObserverResult, UseQueryOptions } from '@tanstack/react-query'; +import { useQuery } from '@tanstack/react-query'; import type { ExceptionsListApiClient } from '../../services/exceptions_list/exceptions_list_api_client'; export function useGetArtifact( diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_list_artifact.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_list_artifact.tsx index 4064c9bdeb82e..0c751cd6525cb 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_list_artifact.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_list_artifact.tsx @@ -6,8 +6,8 @@ */ import type { FoundExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import type { IHttpFetchError } from '@kbn/core-http-browser'; -import type { QueryObserverResult, UseQueryOptions } from 'react-query'; -import { useQuery } from 'react-query'; +import type { QueryObserverResult, UseQueryOptions } from '@tanstack/react-query'; +import { useQuery } from '@tanstack/react-query'; import { useMemo } from 'react'; import { MANAGEMENT_DEFAULT_PAGE, diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_summary_artifact.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_summary_artifact.tsx index 36fc65a309fa6..da4dcf765a8c9 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_summary_artifact.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_summary_artifact.tsx @@ -6,8 +6,8 @@ */ import type { ExceptionListSummarySchema } from '@kbn/securitysolution-io-ts-list-types'; import type { IHttpFetchError } from '@kbn/core-http-browser'; -import type { QueryObserverResult, UseQueryOptions } from 'react-query'; -import { useQuery } from 'react-query'; +import type { QueryObserverResult, UseQueryOptions } from '@tanstack/react-query'; +import { useQuery } from '@tanstack/react-query'; import { parsePoliciesAndFilterToKql, parseQueryFilterToKQL } from '../../common/utils'; import type { ExceptionsListApiClient } from '../../services/exceptions_list/exceptions_list_api_client'; import { DEFAULT_EXCEPTION_LIST_ITEM_SEARCHABLE_FIELDS } from '../../../../common/endpoint/service/artifacts/constants'; diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_update_artifact.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_update_artifact.tsx index 910f2228040a0..ad0791f94f687 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_update_artifact.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_update_artifact.tsx @@ -9,8 +9,8 @@ import type { ExceptionListItemSchema, } from '@kbn/securitysolution-io-ts-list-types'; import type { IHttpFetchError } from '@kbn/core-http-browser'; -import type { UseMutationOptions, UseMutationResult } from 'react-query'; -import { useMutation } from 'react-query'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; +import { useMutation } from '@tanstack/react-query'; import type { ExceptionsListApiClient } from '../../services/exceptions_list/exceptions_list_api_client'; const DEFAULT_OPTIONS = Object.freeze({}); diff --git a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_action_details.test.ts b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_action_details.test.ts index b04e0428037cf..381bd906f58f5 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_action_details.test.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_action_details.test.ts @@ -11,12 +11,12 @@ import { useGetActionDetails } from './use_get_action_details'; import { responseActionsHttpMocks } from '../../mocks/response_actions_http_mocks'; import { resolvePathVariables } from '../../../common/utils/resolve_path_variables'; import { ACTION_DETAILS_ROUTE } from '../../../../common/endpoint/constants'; -import { useQuery as _useQuery } from 'react-query'; +import { useQuery as _useQuery } from '@tanstack/react-query'; const useQueryMock = _useQuery as jest.Mock; -jest.mock('react-query', () => { - const actualReactQueryModule = jest.requireActual('react-query'); +jest.mock('@tanstack/react-query', () => { + const actualReactQueryModule = jest.requireActual('@tanstack/react-query'); return { ...actualReactQueryModule, diff --git a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_action_details.ts b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_action_details.ts index 5bc85d8007176..8336c46e2b232 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_action_details.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_action_details.ts @@ -5,9 +5,9 @@ * 2.0. */ -import type { UseQueryOptions, UseQueryResult } from 'react-query'; +import type { UseQueryOptions, UseQueryResult } from '@tanstack/react-query'; import type { IHttpFetchError } from '@kbn/core-http-browser'; -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import { useHttp } from '../../../common/lib/kibana'; import { resolvePathVariables } from '../../../common/utils/resolve_path_variables'; import { ACTION_DETAILS_ROUTE } from '../../../../common/endpoint/constants'; diff --git a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_action_list.test.ts b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_action_list.test.ts index 8ff8a6f43d11e..d039890256a3d 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_action_list.test.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_action_list.test.ts @@ -9,13 +9,13 @@ import type { AppContextTestRender, ReactQueryHookRenderer } from '../../../comm import { createAppRootMockRenderer } from '../../../common/mock/endpoint'; import { useGetEndpointActionList } from './use_get_endpoint_action_list'; import { ENDPOINTS_ACTION_LIST_ROUTE } from '../../../../common/endpoint/constants'; -import { useQuery as _useQuery } from 'react-query'; +import { useQuery as _useQuery } from '@tanstack/react-query'; import { responseActionsHttpMocks } from '../../mocks/response_actions_http_mocks'; const useQueryMock = _useQuery as jest.Mock; -jest.mock('react-query', () => { - const actualReactQueryModule = jest.requireActual('react-query'); +jest.mock('@tanstack/react-query', () => { + const actualReactQueryModule = jest.requireActual('@tanstack/react-query'); return { ...actualReactQueryModule, diff --git a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_action_list.ts b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_action_list.ts index e0a1e1c1a2485..58420993cb788 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_action_list.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_action_list.ts @@ -5,9 +5,9 @@ * 2.0. */ -import type { UseQueryOptions, UseQueryResult } from 'react-query'; +import type { UseQueryOptions, UseQueryResult } from '@tanstack/react-query'; import type { IHttpFetchError } from '@kbn/core-http-browser'; -import { useQuery } from 'react-query'; +import { useQuery } from '@tanstack/react-query'; import type { EndpointActionListRequestQuery } from '../../../../common/endpoint/schema/actions'; import { useHttp } from '../../../common/lib/kibana'; import { ENDPOINTS_ACTION_LIST_ROUTE } from '../../../../common/endpoint/constants'; diff --git a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_details.test.ts b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_details.test.ts index b9771f7dee3dd..ce8b08014686e 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_details.test.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_details.test.ts @@ -10,13 +10,13 @@ import { createAppRootMockRenderer } from '../../../common/mock/endpoint'; import { useGetEndpointDetails } from './use_get_endpoint_details'; import { resolvePathVariables } from '../../../common/utils/resolve_path_variables'; import { HOST_METADATA_GET_ROUTE } from '../../../../common/endpoint/constants'; -import { useQuery as _useQuery } from 'react-query'; +import { useQuery as _useQuery } from '@tanstack/react-query'; import { endpointMetadataHttpMocks } from '../../pages/endpoint_hosts/mocks'; const useQueryMock = _useQuery as jest.Mock; -jest.mock('react-query', () => { - const actualReactQueryModule = jest.requireActual('react-query'); +jest.mock('@tanstack/react-query', () => { + const actualReactQueryModule = jest.requireActual('@tanstack/react-query'); return { ...actualReactQueryModule, diff --git a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_details.ts b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_details.ts index e81e8b716ea9c..68d53372e9dd3 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_details.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_details.ts @@ -5,8 +5,8 @@ * 2.0. */ -import type { UseQueryOptions, UseQueryResult } from 'react-query'; -import { useQuery } from 'react-query'; +import type { UseQueryOptions, UseQueryResult } from '@tanstack/react-query'; +import { useQuery } from '@tanstack/react-query'; import type { IHttpFetchError } from '@kbn/core-http-browser'; import { resolvePathVariables } from '../../../common/utils/resolve_path_variables'; import { useHttp } from '../../../common/lib/kibana'; diff --git a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_pending_actions_summary.test.ts b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_pending_actions_summary.test.ts index b36cf686d67f5..6bc17c8794616 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_pending_actions_summary.test.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_pending_actions_summary.test.ts @@ -9,13 +9,13 @@ import type { AppContextTestRender, ReactQueryHookRenderer } from '../../../comm import { createAppRootMockRenderer } from '../../../common/mock/endpoint'; import { useGetEndpointPendingActionsSummary } from './use_get_endpoint_pending_actions_summary'; import { ACTION_STATUS_ROUTE } from '../../../../common/endpoint/constants'; -import { useQuery as _useQuery } from 'react-query'; +import { useQuery as _useQuery } from '@tanstack/react-query'; import { responseActionsHttpMocks } from '../../mocks/response_actions_http_mocks'; const useQueryMock = _useQuery as jest.Mock; -jest.mock('react-query', () => { - const actualReactQueryModule = jest.requireActual('react-query'); +jest.mock('@tanstack/react-query', () => { + const actualReactQueryModule = jest.requireActual('@tanstack/react-query'); return { ...actualReactQueryModule, diff --git a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_pending_actions_summary.ts b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_pending_actions_summary.ts index 3254e743da44a..cee29041b0354 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_pending_actions_summary.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_pending_actions_summary.ts @@ -5,8 +5,8 @@ * 2.0. */ -import type { QueryObserverResult, UseQueryOptions } from 'react-query'; -import { useQuery } from 'react-query'; +import type { QueryObserverResult, UseQueryOptions } from '@tanstack/react-query'; +import { useQuery } from '@tanstack/react-query'; import type { IHttpFetchError } from '@kbn/core-http-browser'; import type { PendingActionsResponse } from '../../../../common/endpoint/types'; import { fetchPendingActionsByAgentId } from '../../../common/lib/endpoint_pending_actions'; diff --git a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_policy_response.tsx b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_policy_response.tsx index 7a67d5a446bac..a930dd43eecd4 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_policy_response.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoint_policy_response.tsx @@ -5,8 +5,8 @@ * 2.0. */ import type { IHttpFetchError } from '@kbn/core-http-browser'; -import type { UseQueryResult, UseQueryOptions } from 'react-query'; -import { useQuery } from 'react-query'; +import type { UseQueryResult, UseQueryOptions } from '@tanstack/react-query'; +import { useQuery } from '@tanstack/react-query'; import { useHttp } from '../../../common/lib/kibana'; import { BASE_POLICY_RESPONSE_ROUTE } from '../../../../common/endpoint/constants'; import type { GetHostPolicyResponse } from '../../../../common/endpoint/types'; diff --git a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_send_get_endpoint_processes_request.ts b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_send_get_endpoint_processes_request.ts index aa3f2b69c807b..8fa97e75dc6be 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_send_get_endpoint_processes_request.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_send_get_endpoint_processes_request.ts @@ -5,8 +5,8 @@ * 2.0. */ -import type { UseMutationOptions, UseMutationResult } from 'react-query'; -import { useMutation } from 'react-query'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; +import { useMutation } from '@tanstack/react-query'; import type { IHttpFetchError } from '@kbn/core-http-browser'; import type { ProcessesRequestBody, diff --git a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_send_isolate_endpoint_request.ts b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_send_isolate_endpoint_request.ts index d4ca621a1fc16..b0fb5029ab15d 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_send_isolate_endpoint_request.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_send_isolate_endpoint_request.ts @@ -5,8 +5,8 @@ * 2.0. */ -import type { UseMutationOptions, UseMutationResult } from 'react-query'; -import { useMutation } from 'react-query'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; +import { useMutation } from '@tanstack/react-query'; import type { IHttpFetchError } from '@kbn/core-http-browser'; import { isolateHost } from '../../../common/lib/endpoint_isolation'; import type { diff --git a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_send_kill_process_endpoint_request.ts b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_send_kill_process_endpoint_request.ts index a049d35c21f5c..2d86f15f81d40 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_send_kill_process_endpoint_request.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_send_kill_process_endpoint_request.ts @@ -5,8 +5,8 @@ * 2.0. */ -import type { UseMutationOptions, UseMutationResult } from 'react-query'; -import { useMutation } from 'react-query'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; +import { useMutation } from '@tanstack/react-query'; import type { IHttpFetchError } from '@kbn/core-http-browser'; import type { KillOrSuspendProcessRequestBody, diff --git a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_send_release_endpoint_request.ts b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_send_release_endpoint_request.ts index aaf6e6ab71944..f6d45393ae885 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_send_release_endpoint_request.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_send_release_endpoint_request.ts @@ -5,8 +5,8 @@ * 2.0. */ -import type { UseMutationOptions, UseMutationResult } from 'react-query'; -import { useMutation } from 'react-query'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; +import { useMutation } from '@tanstack/react-query'; import type { IHttpFetchError } from '@kbn/core-http-browser'; import type { HostIsolationRequestBody, diff --git a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_send_suspend_process_endpoint_request.ts b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_send_suspend_process_endpoint_request.ts index 483b0d53e1d6b..d6c2f56cb627f 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_send_suspend_process_endpoint_request.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_send_suspend_process_endpoint_request.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { useMutation } from 'react-query'; -import type { UseMutationOptions, UseMutationResult } from 'react-query'; +import { useMutation } from '@tanstack/react-query'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import type { IHttpFetchError } from '@kbn/core-http-browser'; import type { KillOrSuspendProcessRequestBody, diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details.tsx index a8f343290677e..3a15b6b3873a5 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details.tsx @@ -7,7 +7,7 @@ import { EuiFlyoutBody, EuiFlyoutFooter, EuiLoadingContent, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { memo, useCallback, useEffect, useMemo } from 'react'; -import { ResponseActionsList } from '../../../../components/endpoint_response_actions_list/response_actions_list'; +import { ResponseActionsLog } from '../../../../components/endpoint_response_actions_list/response_actions_log'; import { PolicyResponseWrapper } from '../../../../components/policy_response'; import type { HostMetadata } from '../../../../../../common/endpoint/types'; import { useToasts } from '../../../../../common/lib/kibana'; @@ -82,7 +82,7 @@ export const EndpointDetails = memo(() => { name: 'endpointActivityLog', selected_endpoint: id, }), - content: , + content: , }, ], [ContentLoadingMarkup, hostDetails, policyInfo, hostStatus, queryParams] diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.test.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.test.tsx index bc8ce0c6a3216..f4eac8de48805 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.test.tsx @@ -5,8 +5,7 @@ * 2.0. */ import React from 'react'; -import { act, cleanup } from '@testing-library/react'; -import { fireEvent } from '@testing-library/dom'; +import { act, cleanup, fireEvent } from '@testing-library/react'; import { stubIndexPattern } from '@kbn/data-plugin/common/stubs'; import { useFetchIndex } from '../../../../../common/containers/source'; import { NAME_ERROR } from '../event_filters_list'; @@ -187,9 +186,7 @@ describe('Event filter form', () => { render(); expect(renderResult.queryByText(NAME_ERROR)).toBeNull(); const nameInput = renderResult.getByTestId(`${formPrefix}-name-input`); - act(() => { - fireEvent.blur(nameInput); - }); + fireEvent.blur(nameInput); rerenderWithLatestProps(); expect(renderResult.queryByText(NAME_ERROR)).not.toBeNull(); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form.test.tsx b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form.test.tsx index 43245b340e47c..b60cdf6040b1d 100644 --- a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form.test.tsx @@ -63,24 +63,12 @@ describe('When on the host isolation exceptions entry form', () => { ).resolves.toHaveLength(10); }); - await act(async () => { - await waitFor(() => { - userEvent.click( - renderResult.getByTestId('hostIsolationExceptionsListPage-pageAddButton') - ); - }); - }); - - await act(async () => { - await waitFor(() => { - expect(renderResult.getByTestId('hostIsolationExceptions-form')).toBeTruthy(); - }); + userEvent.click(renderResult.getByTestId('hostIsolationExceptionsListPage-pageAddButton')); - await waitFor(() => { - expect(fleetApiMock.responseProvider.endpointPackagePolicyList).toHaveBeenCalled(); - }); + await waitFor(() => { + expect(renderResult.getByTestId('hostIsolationExceptions-form')).toBeTruthy(); + expect(fleetApiMock.responseProvider.endpointPackagePolicyList).toHaveBeenCalled(); }); - return renderResult; }; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/delete_modal/policy_artifacts_delete_modal.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/delete_modal/policy_artifacts_delete_modal.test.tsx index 6bb63fb96c62a..37f04ff804c1d 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/delete_modal/policy_artifacts_delete_modal.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/delete_modal/policy_artifacts_delete_modal.test.tsx @@ -20,6 +20,7 @@ import { PolicyArtifactsDeleteModal } from './policy_artifacts_delete_modal'; import { exceptionsListAllHttpMocks } from '../../../../../mocks/exceptions_list_http_mocks'; import { ExceptionsListApiClient } from '../../../../../services/exceptions_list/exceptions_list_api_client'; import { POLICY_ARTIFACT_DELETE_MODAL_LABELS } from './translations'; +import { getDeferred } from '../../../../../components/mocks'; const listType: Array = [ 'endpoint_events', @@ -80,6 +81,8 @@ describe.each(listType)('Policy details %s artifact delete modal', (type) => { }); it('should disable the submit button while deleting ', async () => { + const deferred = getDeferred(); + mockedApi.responseProvider.exceptionUpdate.mockDelay.mockReturnValue(deferred.promise); await render(); const confirmButton = renderResult.getByTestId('confirmModalConfirmButton'); userEvent.click(confirmButton); @@ -87,6 +90,10 @@ describe.each(listType)('Policy details %s artifact delete modal', (type) => { await waitFor(() => { expect(confirmButton).toBeDisabled(); }); + + await act(async () => { + deferred.resolve(); // cleanup + }); }); it('should call the API with the removed policy from the exception tags', async () => { diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/delete_modal/policy_artifacts_delete_modal.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/delete_modal/policy_artifacts_delete_modal.tsx index beee26506cd9e..b37fbfe95bd59 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/delete_modal/policy_artifacts_delete_modal.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/delete_modal/policy_artifacts_delete_modal.tsx @@ -6,7 +6,7 @@ */ import { EuiCallOut, EuiConfirmModal, EuiSpacer, EuiText } from '@elastic/eui'; -import { useQueryClient } from 'react-query'; +import { useQueryClient } from '@tanstack/react-query'; import type { IHttpFetchError } from '@kbn/core-http-browser'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import React, { useCallback } from 'react'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/flyout/policy_artifacts_flyout.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/flyout/policy_artifacts_flyout.tsx index c13c351f935ac..d4adb3b53b23a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/flyout/policy_artifacts_flyout.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/flyout/policy_artifacts_flyout.tsx @@ -6,7 +6,7 @@ */ import React, { useCallback, useMemo, useState } from 'react'; -import { useQueryClient } from 'react-query'; +import { useQueryClient } from '@tanstack/react-query'; import { isEmpty, without } from 'lodash/fp'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import { diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/list/policy_artifacts_list.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/list/policy_artifacts_list.test.tsx index 2ccfaff3c8b2b..80e31db13af97 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/list/policy_artifacts_list.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/list/policy_artifacts_list.test.tsx @@ -36,7 +36,8 @@ const getDefaultQueryParameters = (customFilter: string | undefined = '') => ({ }, }); -describe('Policy details artifacts list', () => { +// FLAKY: https://github.com/elastic/kibana/issues/139183 +describe.skip('Policy details artifacts list', () => { let render: (externalPrivileges?: boolean) => Promise>; let renderResult: ReturnType; let history: AppContextTestRender['history']; @@ -67,7 +68,7 @@ describe('Policy details artifacts list', () => { getArtifactPath={getEventFiltersListPath} /> ); - await waitFor(mockedApi.responseProvider.eventFiltersList); + await waitFor(() => expect(mockedApi.responseProvider.eventFiltersList).toHaveBeenCalled()); }); return renderResult; }; @@ -99,7 +100,9 @@ describe('Policy details artifacts list', () => { it('should expand an item when expand is clicked', async () => { await render(); - expect(renderResult.getAllByTestId('artifacts-collapsed-list-card')).toHaveLength(1); + await waitFor(() => { + expect(renderResult.getAllByTestId('artifacts-collapsed-list-card')).toHaveLength(1); + }); userEvent.click( renderResult.getByTestId('artifacts-collapsed-list-card-header-expandCollapse') diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_integration_artifacts_card.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_integration_artifacts_card.test.tsx index 2e662b393c92f..2ac194b39bd8e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_integration_artifacts_card.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_integration_artifacts_card.test.tsx @@ -69,9 +69,11 @@ describe('Fleet integration policy endpoint security event filters card', () => ); await render(); - expect(renderResult.getByTestId('artifacts-fleet-integration-card')).toHaveTextContent( - 'Event filters3' - ); + await waitFor(() => { + expect(renderResult.getByTestId('artifacts-fleet-integration-card')).toHaveTextContent( + 'Event filters3' + ); + }); }); it('should show the card even when no event filters associated with the policy', async () => { diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx index 227e4eb3a78cd..eab38e3cdd7b5 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx @@ -23,7 +23,9 @@ import { APP_UI_ID } from '../../../../../common/constants'; import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { exceptionsFindHttpMocks } from '../../../mocks/exceptions_list_http_mocks'; -jest.mock('./policy_forms/components/policy_form_layout'); +jest.mock('./policy_forms/components/policy_form_layout', () => ({ + PolicyFormLayout: () => <>, +})); jest.mock('../../../../common/components/user_privileges'); jest.mock('../../../../common/hooks/use_experimental_features'); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.test.tsx index 122ef57fa4a3e..66137aab4cea1 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.test.tsx @@ -42,7 +42,7 @@ describe('When on the policy list page', () => { describe('and there are no policies', () => { beforeEach(async () => { - getPackagePolicies.mockImplementation(() => + getPackagePolicies.mockResolvedValue( sendGetEndpointSpecificPackagePoliciesMock({ page: 1, perPage: 20, @@ -60,24 +60,24 @@ describe('When on the policy list page', () => { it('should show the empty page', () => { expect(renderResult.getByTestId('emptyPolicyTable')).toBeTruthy(); }); - it('should show instruction text and a button to add the Endpoint Security integration', () => { + it('should show instruction text and a button to add the Endpoint Security integration', async () => { expect( renderResult.findByText( 'From this page, you’ll be able to view and manage the Endpoint and Cloud Security Integration policies in your environment running Endpoint and Cloud Security.' ) ).toBeTruthy(); - expect(renderResult.getByTestId('onboardingStartButton')).toBeTruthy(); + await waitFor(() => { + expect(renderResult.getByTestId('onboardingStartButton')).toBeTruthy(); + }); }); }); describe('and data exists', () => { - let policies: GetPolicyListResponse; + const policies: GetPolicyListResponse = sendGetEndpointSpecificPackagePoliciesMock(); + beforeEach(async () => { - policies = await sendGetEndpointSpecificPackagePoliciesMock(); - getPackagePolicies.mockImplementation(async () => { - return policies; - }); - getAgentPolicies.mockResolvedValue({ + getPackagePolicies.mockReturnValue(policies); + getAgentPolicies.mockReturnValue({ items: [ { package_policies: [policies.items[0].id], agents: 4 }, { package_policies: [policies.items[1].id], agents: 2 }, @@ -118,7 +118,7 @@ describe('When on the policy list page', () => { expect(updatedByCells[0].textContent).toEqual(expectedAvatarName.charAt(0)); expect(firstUpdatedByName.textContent).toEqual(expectedAvatarName); }); - it('should show the correct endpoint count', () => { + it('should show the correct endpoint count', async () => { const endpointCount = renderResult.getAllByTestId('policyEndpointCountLink'); expect(endpointCount[0].textContent).toBe('4'); }); @@ -149,26 +149,31 @@ describe('When on the policy list page', () => { render(); }); }); - describe('pagination', () => { + // FLAKY: https://github.com/elastic/kibana/issues/139207 + describe.skip('pagination', () => { beforeEach(async () => { - getPackagePolicies.mockImplementation(async ({ page, perPage }) => { + getPackagePolicies.mockImplementation(({ page, perPage }) => { // # policies = 100 to trigger UI to show pagination - const response = await sendGetEndpointSpecificPackagePoliciesMock({ + return sendGetEndpointSpecificPackagePoliciesMock({ page, perPage, count: 100, }); - return response; }); render(); await waitFor(() => { expect(getPackagePolicies).toHaveBeenCalled(); + expect(sendGetEndpointSpecificPackagePolicies).toHaveBeenCalled(); + expect(sendGetAgentPolicyList).toHaveBeenCalled(); }); }); afterEach(() => { getPackagePolicies.mockReset(); }); it('should pass the correct page value to the api', async () => { + await waitFor(() => { + expect(renderResult.getByTestId('pagination-button-next')).toBeTruthy(); + }); act(() => { renderResult.getByTestId('pagination-button-next').click(); }); @@ -181,6 +186,9 @@ describe('When on the policy list page', () => { }); }); it('should pass the correct pageSize value to the api', async () => { + await waitFor(() => { + expect(renderResult.getByTestId('tablePaginationPopoverButton')).toBeTruthy(); + }); act(() => { renderResult.getByTestId('tablePaginationPopoverButton').click(); }); @@ -203,10 +211,10 @@ describe('When on the policy list page', () => { }); await waitFor(() => { expect(getPackagePolicies).toHaveBeenCalledTimes(2); - }); - expect(getPackagePolicies.mock.calls[1][1].query).toEqual({ - page: 3, - perPage: 50, + expect(getPackagePolicies.mock.calls[1][1].query).toEqual({ + page: 3, + perPage: 50, + }); }); }); it('should reset page back to 1 if the user is on a page > 1 and they change page size', async () => { @@ -215,24 +223,23 @@ describe('When on the policy list page', () => { history.push('/administration/policies?page=2&pageSize=20'); }); await waitFor(() => { - expect(getPackagePolicies).toHaveBeenCalledTimes(2); + expect(getPackagePolicies).toHaveBeenCalled(); }); // change pageSize - act(() => { - renderResult.getByTestId('tablePaginationPopoverButton').click(); + await act(async () => { + (await renderResult.getByTestId('tablePaginationPopoverButton')).click(); }); const pageSize10 = await renderResult.findByTestId('tablePagination-10-rows'); act(() => { pageSize10.click(); }); - await waitFor(() => { - expect(getPackagePolicies).toHaveBeenCalledTimes(3); - }); - expect(getPackagePolicies.mock.calls[2][1].query).toEqual({ - page: 1, - perPage: 10, + expect(sendGetEndpointSpecificPackagePolicies).toHaveBeenLastCalledWith(expect.any(Object), { + query: { + page: 1, + perPage: 10, + }, }); }); it('should set page to 1 if user tries to force an invalid page number', async () => { diff --git a/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.tsx b/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.tsx index cb1c7d53e05a6..c72e69023af77 100644 --- a/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/response_actions/view/response_actions_list_page.tsx @@ -7,13 +7,13 @@ import React from 'react'; import { AdministrationListPage } from '../../../components/administration_list_page'; -import { ResponseActionsList } from '../../../components/endpoint_response_actions_list/response_actions_list'; +import { ResponseActionsLog } from '../../../components/endpoint_response_actions_list/response_actions_log'; import { UX_MESSAGES } from '../../../components/endpoint_response_actions_list/translations'; export const ResponseActionsListPage = () => { return ( - + ); }; diff --git a/x-pack/plugins/security_solution/public/management/services/policies/hooks.test.ts b/x-pack/plugins/security_solution/public/management/services/policies/hooks.test.ts index b591051d72c94..8bd8c4c72efea 100644 --- a/x-pack/plugins/security_solution/public/management/services/policies/hooks.test.ts +++ b/x-pack/plugins/security_solution/public/management/services/policies/hooks.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { UseQueryOptions } from 'react-query'; +import type { UseQueryOptions } from '@tanstack/react-query'; import type { IHttpFetchError, HttpSetup } from '@kbn/core-http-browser'; import type { GetPackagesResponse } from '@kbn/fleet-plugin/common'; import { useGetEndpointSecurityPackage } from './hooks'; diff --git a/x-pack/plugins/security_solution/public/management/services/policies/hooks.ts b/x-pack/plugins/security_solution/public/management/services/policies/hooks.ts index 6b2c726875827..fef45b657d5da 100644 --- a/x-pack/plugins/security_solution/public/management/services/policies/hooks.ts +++ b/x-pack/plugins/security_solution/public/management/services/policies/hooks.ts @@ -4,8 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { QueryObserverResult, UseQueryOptions } from 'react-query'; -import { useQuery } from 'react-query'; +import type { QueryObserverResult, UseQueryOptions } from '@tanstack/react-query'; +import { useQuery } from '@tanstack/react-query'; import type { IHttpFetchError } from '@kbn/core-http-browser'; import type { GetAgentPoliciesResponse, GetPackagesResponse } from '@kbn/fleet-plugin/common'; import { AGENT_POLICY_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common'; diff --git a/x-pack/plugins/security_solution/public/management/services/policies/test_mock_utils.ts b/x-pack/plugins/security_solution/public/management/services/policies/test_mock_utils.ts index b415fceedc0cd..af7a531d5a352 100644 --- a/x-pack/plugins/security_solution/public/management/services/policies/test_mock_utils.ts +++ b/x-pack/plugins/security_solution/public/management/services/policies/test_mock_utils.ts @@ -7,13 +7,13 @@ import { FleetPackagePolicyGenerator } from '../../../../common/endpoint/data_generators/fleet_package_policy_generator'; import type { GetPolicyListResponse } from '../../pages/policy/types'; -export const sendGetEndpointSpecificPackagePoliciesMock = async ( +export const sendGetEndpointSpecificPackagePoliciesMock = ( params: { page: number; perPage: number; count: number; } = { page: 1, perPage: 20, count: 5 } -): Promise => { +): GetPolicyListResponse => { const { page, perPage, count } = params; const generator = new FleetPackagePolicyGenerator(); const items = Array.from({ length: count }, (_, index) => { diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.tsx b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.tsx index a7e2f67e5f586..53c69b5b47afd 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.tsx +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.tsx @@ -8,7 +8,7 @@ import { EuiAccordion, EuiLink, EuiText } from '@elastic/eui'; import deepEqual from 'fast-deep-equal'; import React, { useCallback, useEffect, useState, useMemo } from 'react'; -import { createPortalNode, InPortal } from 'react-reverse-portal'; +import { createHtmlPortalNode, InPortal } from 'react-reverse-portal'; import styled, { css } from 'styled-components'; import type { Filter, Query } from '@kbn/es-query'; @@ -127,7 +127,7 @@ export const EmbeddedMapComponent = ({ // own component tree instead of the embeddables (default). This is necessary to have access to // the Redux store, theme provider, etc, which is required to register and un-register the draggable // Search InPortal/OutPortal for implementation touch points - const portalNode = React.useMemo(() => createPortalNode(), []); + const portalNode = React.useMemo(() => createHtmlPortalNode(), []); useEffect(() => { setMapIndexPatterns((prevMapIndexPatterns) => { diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.test.tsx b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.test.tsx index b7e27b2422451..c82051b11769c 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.test.tsx @@ -7,7 +7,7 @@ import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks'; import { createEmbeddable, findMatchingIndexPatterns } from './embedded_map_helpers'; -import { createPortalNode } from 'react-reverse-portal'; +import { createHtmlPortalNode } from 'react-reverse-portal'; import { mockAPMIndexPattern, mockAPMRegexIndexPattern, @@ -43,7 +43,7 @@ describe('embedded_map_helpers', () => { '2020-07-07T08:20:18.966Z', '2020-07-08T08:20:18.966Z', setQueryMock, - createPortalNode(), + createHtmlPortalNode(), mockEmbeddable ); expect(setQueryMock).toHaveBeenCalledTimes(1); @@ -58,7 +58,7 @@ describe('embedded_map_helpers', () => { '2020-07-07T08:20:18.966Z', '2020-07-08T08:20:18.966Z', setQueryMock, - createPortalNode(), + createHtmlPortalNode(), mockEmbeddable ); expect(setQueryMock.mock.calls[0][0].refetch).not.toBe(embeddable.reload); diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.tsx b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.tsx index 731adaa9b5081..3583e54f4c980 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.tsx +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.tsx @@ -7,7 +7,7 @@ import uuid from 'uuid'; import React from 'react'; -import type { PortalNode } from 'react-reverse-portal'; +import type { HtmlPortalNode } from 'react-reverse-portal'; import { OutPortal } from 'react-reverse-portal'; import minimatch from 'minimatch'; import type { Filter, Query } from '@kbn/es-query'; @@ -51,7 +51,7 @@ export const createEmbeddable = async ( startDate: GlobalTimeArgs['from'], endDate: GlobalTimeArgs['to'], setQuery: GlobalTimeArgs['setQuery'], - portalNode: PortalNode, + portalNode: HtmlPortalNode, embeddableApi: EmbeddableStart ): Promise => { const factory = embeddableApi.getEmbeddableFactory< diff --git a/x-pack/plugins/security_solution/public/network/containers/network_dns/index.test.tsx b/x-pack/plugins/security_solution/public/network/containers/network_dns/index.test.tsx index 34272cd7cbccf..aa260236da39d 100644 --- a/x-pack/plugins/security_solution/public/network/containers/network_dns/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/network_dns/index.test.tsx @@ -17,7 +17,6 @@ const mockUseSearchStrategy = useSearchStrategy as jest.Mock; const mockSearch = jest.fn(); const props = { - docValueFields: [], endDate: '2020-07-08T08:20:18.966Z', id: ID, indexNames: ['auditbeat-*'], diff --git a/x-pack/plugins/security_solution/public/network/containers/network_dns/index.tsx b/x-pack/plugins/security_solution/public/network/containers/network_dns/index.tsx index 28dc7e657191c..833efc839b600 100644 --- a/x-pack/plugins/security_solution/public/network/containers/network_dns/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/network_dns/index.tsx @@ -15,7 +15,6 @@ import { createFilter } from '../../../common/containers/helpers'; import { generateTablePaginationOptions } from '../../../common/components/paginated_table/helpers'; import { networkSelectors } from '../../store'; import type { - DocValueFields, NetworkDnsRequestOptions, NetworkDnsEdges, PageInfoPaginated, @@ -41,7 +40,6 @@ export interface NetworkDnsResponse { interface UseNetworkDns { id: string; - docValueFields: DocValueFields[]; indexNames: string[]; filterQuery?: ESTermQuery | string; endDate: string; @@ -50,7 +48,6 @@ interface UseNetworkDns { } export const useNetworkDns = ({ - docValueFields, endDate, filterQuery, id, @@ -119,7 +116,6 @@ export const useNetworkDns = ({ const myRequest = { ...(prevRequest ?? {}), defaultIndex: indexNames, - docValueFields: docValueFields ?? [], isPtrIncluded, factoryQueryType: NetworkQueries.dns, filterQuery: createFilter(filterQuery), @@ -136,17 +132,7 @@ export const useNetworkDns = ({ } return prevRequest; }); - }, [ - activePage, - indexNames, - endDate, - filterQuery, - limit, - startDate, - sort, - isPtrIncluded, - docValueFields, - ]); + }, [activePage, indexNames, endDate, filterQuery, limit, startDate, sort, isPtrIncluded]); useEffect(() => { if (!skip && networkDnsRequest) { diff --git a/x-pack/plugins/security_solution/public/network/pages/navigation/dns_query_tab_body.tsx b/x-pack/plugins/security_solution/public/network/pages/navigation/dns_query_tab_body.tsx index ee423b0343088..54bdb1acd81b6 100644 --- a/x-pack/plugins/security_solution/public/network/pages/navigation/dns_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/navigation/dns_query_tab_body.tsx @@ -51,7 +51,6 @@ export const histogramConfigs: Omit = { const DnsQueryTabBodyComponent: React.FC = ({ deleteQuery, - docValueFields, endDate, filterQuery, indexNames, @@ -82,7 +81,6 @@ const DnsQueryTabBodyComponent: React.FC = ({ loading, { totalCount, networkDns, pageInfo, loadPage, id, inspect, isInspected, refetch }, ] = useNetworkDns({ - docValueFields: docValueFields ?? [], endDate, filterQuery, id: queryId, @@ -109,7 +107,6 @@ const DnsQueryTabBodyComponent: React.FC = ({ ( - ({ - docValueFields, - type, - to, - filterQuery, - isInitializing, - from, - indexPattern, - indexNames, - setQuery, - }) => { + ({ type, to, filterQuery, isInitializing, from, indexPattern, indexNames, setQuery }) => { const networkAnomaliesFilterQuery = { bool: { should: [ @@ -83,7 +73,7 @@ export const NetworkRoutes = React.memo( return ( - + <> diff --git a/x-pack/plugins/security_solution/public/network/pages/navigation/types.ts b/x-pack/plugins/security_solution/public/network/pages/navigation/types.ts index e2b96fce68b6e..941ace5049cdf 100644 --- a/x-pack/plugins/security_solution/public/network/pages/navigation/types.ts +++ b/x-pack/plugins/security_solution/public/network/pages/navigation/types.ts @@ -15,8 +15,6 @@ import type { FlowTargetSourceDest } from '../../../../common/search_strategy/se import type { networkModel } from '../../store'; import type { GlobalTimeArgs } from '../../../common/containers/use_global_time'; -import type { DocValueFields } from '../../../common/containers/source'; - export interface QueryTabBodyProps extends Pick { endDate: string; filterQuery?: string | ESTermQuery; @@ -27,9 +25,7 @@ export interface QueryTabBodyProps extends Pick( [dispatch] ); - const { docValueFields, indicesExist, indexPattern, selectedPatterns } = useSourcererDataView(); + const { indicesExist, indexPattern, selectedPatterns } = useSourcererDataView(); const onSkipFocusBeforeEventsTable = useCallback(() => { containerElement.current @@ -214,7 +214,6 @@ const NetworkComponent = React.memo( { beforeEach(() => { jest.clearAllMocks(); }); - test('init', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook< - UseCasesByStatusProps, - UseCasesByStatusResults - >(() => useCasesByStatus({ skip: false }), { + test('init', () => { + const { result } = renderHook( + () => useCasesByStatus({}), + { wrapper: TestProviders, - }); - await waitForNextUpdate(); - expect(result.current).toEqual({ - closed: 0, - inProgress: 0, - isLoading: true, - open: 0, - totalCounts: 0, - updatedAt: dateNow, - }); + } + ); + expect(result.current).toEqual({ + closed: 0, + inProgress: 0, + isLoading: true, + open: 0, + totalCounts: 0, + updatedAt: dateNow, }); }); test('fetch data', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook< - UseCasesByStatusProps, - UseCasesByStatusResults - >(() => useCasesByStatus({ skip: false }), { - wrapper: TestProviders, - }); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(result.current).toEqual({ - closed: 3, - inProgress: 2, - isLoading: false, - open: 1, - totalCounts: 6, - updatedAt: dateNow, - }); + const { result, waitForNextUpdate } = renderHook< + UseCasesByStatusProps, + UseCasesByStatusResults + >(() => useCasesByStatus({ skip: false }), { + wrapper: TestProviders, + }); + await waitForNextUpdate(); + expect(result.current).toEqual({ + closed: 3, + inProgress: 2, + isLoading: false, + open: 1, + totalCounts: 6, + updatedAt: dateNow, }); }); test('it should call setQuery when fetching', async () => { - await act(async () => { - const { waitForNextUpdate } = renderHook( - () => useCasesByStatus({ skip: false }), - { - wrapper: TestProviders, - } - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(mockSetQuery).toHaveBeenCalled(); - }); + const { waitForNextUpdate } = renderHook( + () => useCasesByStatus({ skip: false }), + { + wrapper: TestProviders, + } + ); + await waitForNextUpdate(); + expect(mockSetQuery).toHaveBeenCalled(); }); test('it should call deleteQuery when unmounting', async () => { - await act(async () => { - const { waitForNextUpdate, unmount } = renderHook< - UseCasesByStatusProps, - UseCasesByStatusResults - >(() => useCasesByStatus({ skip: false }), { - wrapper: TestProviders, - }); - await waitForNextUpdate(); + const { waitForNextUpdate, unmount } = renderHook< + UseCasesByStatusProps, + UseCasesByStatusResults + >(() => useCasesByStatus({ skip: false }), { + wrapper: TestProviders, + }); + await waitForNextUpdate(); - unmount(); + unmount(); - expect(mockDeleteQuery).toHaveBeenCalled(); - }); + expect(mockDeleteQuery).toHaveBeenCalled(); }); test('skip', async () => { const abortSpy = jest.spyOn(AbortController.prototype, 'abort'); - await act(async () => { - const localProps = { skip: false }; + const localProps = { skip: false }; - const { rerender, waitForNextUpdate } = renderHook< - UseCasesByStatusProps, - UseCasesByStatusResults - >(() => useCasesByStatus(localProps), { - wrapper: TestProviders, - }); - await waitForNextUpdate(); - await waitForNextUpdate(); - - localProps.skip = true; - act(() => rerender()); - act(() => rerender()); - expect(abortSpy).toHaveBeenCalledTimes(2); + const { rerender, waitForNextUpdate } = renderHook< + UseCasesByStatusProps, + UseCasesByStatusResults + >(() => useCasesByStatus(localProps), { + wrapper: TestProviders, }); + await waitForNextUpdate(); + + localProps.skip = true; + act(() => rerender()); + act(() => rerender()); + expect(abortSpy).toHaveBeenCalledTimes(2); }); }); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/use_case_items.test.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/use_case_items.test.ts index 1e9b742f70600..036ed835b46cf 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/use_case_items.test.ts +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/use_case_items.test.ts @@ -63,17 +63,14 @@ describe('useCaseItems', () => { }); it('should return default values', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderUseCaseItems(); + const { result, waitForNextUpdate } = renderUseCaseItems(); - await waitForNextUpdate(); - await waitForNextUpdate(); + await waitForNextUpdate(); - expect(result.current).toEqual({ - items: [], - isLoading: false, - updatedAt: dateNow, - }); + expect(result.current).toEqual({ + items: [], + isLoading: false, + updatedAt: dateNow, }); expect(mockCasesApi).toBeCalledWith({ @@ -89,44 +86,36 @@ describe('useCaseItems', () => { it('should return parsed items', async () => { mockCasesApi.mockReturnValue(mockCasesResult); + const { result, waitForNextUpdate } = renderUseCaseItems(); - await act(async () => { - const { result, waitForNextUpdate } = renderUseCaseItems(); + await waitForNextUpdate(); - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(result.current).toEqual({ - items: parsedCasesItems, - isLoading: false, - updatedAt: dateNow, - }); + expect(result.current).toEqual({ + items: parsedCasesItems, + isLoading: false, + updatedAt: dateNow, }); }); test('it should call setQuery when fetching', async () => { mockCasesApi.mockReturnValue(mockCasesResult); - await act(async () => { - const { waitForNextUpdate } = renderUseCaseItems(); + const { waitForNextUpdate } = renderUseCaseItems(); - await waitForNextUpdate(); - await waitForNextUpdate(); + await waitForNextUpdate(); - expect(mockSetQuery).toHaveBeenCalled(); - }); + expect(mockSetQuery).toHaveBeenCalled(); }); test('it should call deleteQuery when unmounting', async () => { - await act(async () => { - const { waitForNextUpdate, unmount } = renderUseCaseItems(); + const { waitForNextUpdate, unmount } = renderUseCaseItems(); - await waitForNextUpdate(); - await waitForNextUpdate(); + await waitForNextUpdate(); + act(() => { unmount(); - - expect(mockDeleteQuery).toHaveBeenCalled(); }); + + expect(mockDeleteQuery).toHaveBeenCalled(); }); it('should return new updatedAt', async () => { @@ -135,18 +124,15 @@ describe('useCaseItems', () => { mockDateNow.mockReturnValueOnce(dateNow); mockCasesApi.mockReturnValue(mockCasesResult); - await act(async () => { - const { result, waitForNextUpdate } = renderUseCaseItems(); + const { result, waitForNextUpdate } = renderUseCaseItems(); - await waitForNextUpdate(); - await waitForNextUpdate(); + await waitForNextUpdate(); - expect(mockDateNow).toHaveBeenCalled(); - expect(result.current).toEqual({ - items: parsedCasesItems, - isLoading: false, - updatedAt: newDateNow, - }); + expect(mockDateNow).toHaveBeenCalled(); + expect(result.current).toEqual({ + items: parsedCasesItems, + isLoading: false, + updatedAt: newDateNow, }); }); diff --git a/x-pack/plugins/security_solution/public/threat_intelligence/routes.tsx b/x-pack/plugins/security_solution/public/threat_intelligence/routes.tsx index a6189921665eb..167f2c8d67476 100644 --- a/x-pack/plugins/security_solution/public/threat_intelligence/routes.tsx +++ b/x-pack/plugins/security_solution/public/threat_intelligence/routes.tsx @@ -8,8 +8,9 @@ import React, { memo } from 'react'; import { Redirect } from 'react-router-dom'; import { TrackApplicationView } from '@kbn/usage-collection-plugin/public'; -import type { ThreatIntelligenceSecuritySolutionContext } from '@kbn/threat-intelligence-plugin/public'; +import type { SecuritySolutionPluginContext } from '@kbn/threat-intelligence-plugin/public'; import { THREAT_INTELLIGENCE_BASE_PATH } from '@kbn/threat-intelligence-plugin/public'; +import type { SourcererDataView } from '@kbn/threat-intelligence-plugin/public/types'; import { useKibana } from '../common/lib/kibana'; import { FiltersGlobal } from '../common/components/filters_global'; import { SpyRoute } from '../common/utils/route/spy_routes'; @@ -18,19 +19,23 @@ import { useIsExperimentalFeatureEnabled } from '../common/hooks/use_experimenta import { licenseService } from '../common/hooks/use_license'; import { SecurityPageName } from '../app/types'; import type { SecuritySubPluginRoutes } from '../app/types'; +import { useSourcererDataView } from '../common/containers/sourcerer'; const ThreatIntelligence = memo(() => { const { threatIntelligence } = useKibana().services; const ThreatIntelligencePlugin = threatIntelligence.getComponent(); + const sourcererDataView = useSourcererDataView(); + const enabled = useIsExperimentalFeatureEnabled('threatIntelligenceEnabled'); if (!enabled) { return ; } - const securitySolutionContext: ThreatIntelligenceSecuritySolutionContext = { + const securitySolutionContext: SecuritySolutionPluginContext = { getFiltersGlobalComponent: () => FiltersGlobal, licenseService, + sourcererDataView: sourcererDataView as unknown as SourcererDataView, }; return ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.tsx index 04bd9d506b63e..74662e7563201 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.tsx @@ -57,6 +57,9 @@ const AddTimelineButtonComponent: React.FC = ({ anchorPosition="downRight" button={PopoverButtonIcon} id="timelineSettingsPopover" + panelProps={{ + 'data-test-subj': 'timeline-addPopupPanel', + }} isOpen={showActions} closePopover={onClosePopover} ownFocus diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.test.tsx index 42306a7d3d205..79ef41a070574 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.test.tsx @@ -14,7 +14,7 @@ import { TimelineId } from '../../../../../common/types/timeline'; import { useTimelineKpis } from '../../../containers/kpis'; import { FlyoutHeader } from '.'; import { useSourcererDataView } from '../../../../common/containers/sourcerer'; -import { mockBrowserFields, mockDocValueFields } from '../../../../common/containers/source/mock'; +import { mockBrowserFields } from '../../../../common/containers/source/mock'; import { getEmptyValue } from '../../../../common/components/empty_value'; import { allCasesPermissions, readCasesPermissions } from '../../../../cases_test_utils'; @@ -53,7 +53,6 @@ const mockUseTimelineLargeKpiResponse = { }; const defaultMocks = { browserFields: mockBrowserFields, - docValueFields: mockDocValueFields, indexPattern: mockIndexPattern, loading: false, selectedPatterns: mockIndexNames, diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx index 4dc62c43e7f1e..743a1e87f9f8a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx @@ -345,7 +345,7 @@ const TimelineStatusInfoComponent: React.FC = ({ timelineId } const TimelineStatusInfo = React.memo(TimelineStatusInfoComponent); const FlyoutHeaderComponent: React.FC = ({ timelineId }) => { - const { selectedPatterns, indexPattern, docValueFields, browserFields } = useSourcererDataView( + const { selectedPatterns, indexPattern, browserFields } = useSourcererDataView( SourcererScopeName.timeline ); const getStartSelector = useMemo(() => startSelector(), []); @@ -409,7 +409,6 @@ const FlyoutHeaderComponent: React.FC = ({ timelineId }) => { const [loading, kpis] = useTimelineKpis({ defaultIndex: selectedPatterns, - docValueFields, timerange, isBlankTimeline, filterQuery: combinedQueries?.filterQuery ?? '', diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/index.test.tsx index c2036263ec28d..aae85b19bfd05 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/index.test.tsx @@ -194,7 +194,6 @@ describe('Timeline', () => { test('it does render the timeline table when the source is loading with no events', () => { (useSourcererDataView as jest.Mock).mockReturnValue({ browserFields: {}, - docValueFields: [], loading: true, indexPattern: {}, selectedPatterns: [], diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx index d8c7c5e34c908..310849aee4c09 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx @@ -11,7 +11,7 @@ import useResizeObserver from 'use-resize-observer/polyfilled'; import { DragDropContextWrapper } from '../../../common/components/drag_and_drop/drag_drop_context_wrapper'; import '../../../common/mock/match_media'; -import { mockBrowserFields, mockDocValueFields } from '../../../common/containers/source/mock'; +import { mockBrowserFields } from '../../../common/containers/source/mock'; import { TimelineId } from '../../../../common/types/timeline'; import { createSecuritySolutionStorageMock, @@ -38,7 +38,9 @@ jest.mock('../../containers', () => ({ useTimelineEvents: jest.fn(), })); -jest.mock('./tabs_content'); +jest.mock('./tabs_content', () => ({ + TabsContent: () =>
, +})); jest.mock('../../../common/lib/kibana'); const originalKibanaLib = jest.requireActual('../../../common/lib/kibana'); @@ -92,7 +94,6 @@ jest.mock('../../../common/containers/sourcerer'); const mockDataView = { dataViewId: mockGlobalState.timeline.timelineById.test?.dataViewId, browserFields: mockBrowserFields, - docValueFields: mockDocValueFields, loading: false, indexPattern: mockIndexPattern, pageInfo: { activePage: 0, querySize: 0 }, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.test.tsx index 76d3ceb240883..b7f9d74a9019c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.test.tsx @@ -40,7 +40,7 @@ jest.mock('../../../../common/lib/kibana', () => ({ })); describe('NewTimeline', () => { - const mockGetButton = jest.fn(); + const mockGetButton = jest.fn().mockReturnValue('<>'); const props: NewTimelineProps = { closeGearMenu: jest.fn(), diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx index 831b8358bad26..c7a30a4f501b8 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx @@ -206,7 +206,6 @@ describe('Timeline', () => { test('it does render the timeline table when the source is loading with no events', () => { (useSourcererDataView as jest.Mock).mockReturnValue({ browserFields: {}, - docValueFields: [], loading: true, indexPattern: {}, selectedPatterns: [], diff --git a/x-pack/plugins/security_solution/public/timelines/containers/kpis/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/kpis/index.tsx index 5a4a61e809bd1..5bd71f4f7be94 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/kpis/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/kpis/index.tsx @@ -14,7 +14,6 @@ import { isCompleteResponse, isErrorResponse } from '@kbn/data-plugin/public'; import type { inputsModel } from '../../../common/store'; import { useKibana } from '../../../common/lib/kibana'; import type { - DocValueFields, TimelineKpiStrategyRequest, TimelineKpiStrategyResponse, TimerangeInput, @@ -28,14 +27,12 @@ export interface UseTimelineKpiProps { timerange: TimerangeInput; filterQuery?: ESQuery | string | undefined; defaultIndex: string[]; - docValueFields?: DocValueFields[]; isBlankTimeline: boolean; } export const useTimelineKpis = ({ timerange, filterQuery, - docValueFields, defaultIndex, isBlankTimeline, }: UseTimelineKpiProps): [boolean, TimelineKpiStrategyResponse | null] => { @@ -96,7 +93,6 @@ export const useTimelineKpis = ({ setTimelineKpiRequest((prevRequest) => { const myRequest = { ...(prevRequest ?? {}), - docValueFields, defaultIndex, timerange, filterQuery, @@ -107,7 +103,7 @@ export const useTimelineKpis = ({ } return prevRequest; }); - }, [docValueFields, defaultIndex, timerange, filterQuery]); + }, [defaultIndex, timerange, filterQuery]); useEffect(() => { if (!isBlankTimeline) { diff --git a/x-pack/plugins/security_solution/public/users/pages/navigation/all_users_query_tab_body.tsx b/x-pack/plugins/security_solution/public/users/pages/navigation/all_users_query_tab_body.tsx index 0030391a2b54a..7c203d5920706 100644 --- a/x-pack/plugins/security_solution/public/users/pages/navigation/all_users_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/users/pages/navigation/all_users_query_tab_body.tsx @@ -32,7 +32,6 @@ export const AllUsersQueryTabBody = ({ setQuery, startDate, type, - docValueFields, deleteQuery, }: UsersComponentsQueryProps) => { const { toggleStatus } = useQueryToggle(QUERY_ID); @@ -70,7 +69,6 @@ export const AllUsersQueryTabBody = ({ search({ filterQuery, defaultIndex: indexNames, - docValueFields, timerange: { interval: '12h', from: startDate, @@ -80,18 +78,7 @@ export const AllUsersQueryTabBody = ({ sort, }); } - }, [ - search, - startDate, - endDate, - filterQuery, - indexNames, - querySkip, - docValueFields, - activePage, - limit, - sort, - ]); + }, [search, startDate, endDate, filterQuery, indexNames, querySkip, activePage, limit, sort]); return ( { @@ -47,7 +46,6 @@ export const AuthenticationsQueryTabBody = ({ startDate={startDate} type={type} skip={skip} - docValueFields={docValueFields} userName={userName} /> diff --git a/x-pack/plugins/security_solution/public/users/pages/navigation/types.ts b/x-pack/plugins/security_solution/public/users/pages/navigation/types.ts index aeac9326a1f93..4bc79174beba6 100644 --- a/x-pack/plugins/security_solution/public/users/pages/navigation/types.ts +++ b/x-pack/plugins/security_solution/public/users/pages/navigation/types.ts @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { DocValueFields } from '@kbn/timelines-plugin/common'; import type { UsersTableType, UsersType } from '../../store/model'; import type { GlobalTimeArgs } from '../../../common/containers/use_global_time'; import type { ESTermQuery } from '../../../../common/typed_json'; @@ -22,7 +21,6 @@ export interface QueryTabBodyProps { export type UsersComponentsQueryProps = QueryTabBodyProps & { deleteQuery?: GlobalTimeArgs['deleteQuery']; - docValueFields?: DocValueFields[]; indexNames: string[]; skip: boolean; setQuery: GlobalTimeArgs['setQuery']; diff --git a/x-pack/plugins/security_solution/public/users/pages/types.ts b/x-pack/plugins/security_solution/public/users/pages/types.ts index b7af4c3c54d22..955b565b328a8 100644 --- a/x-pack/plugins/security_solution/public/users/pages/types.ts +++ b/x-pack/plugins/security_solution/public/users/pages/types.ts @@ -6,13 +6,11 @@ */ import type { Filter } from '@kbn/es-query'; -import type { DocValueFields } from '@kbn/timelines-plugin/common'; import type { GlobalTimeArgs } from '../../common/containers/use_global_time'; import type { usersModel } from '../store'; export type UsersTabsProps = GlobalTimeArgs & { - docValueFields: DocValueFields[]; filterQuery: string; pageFilters?: Filter[]; indexNames: string[]; diff --git a/x-pack/plugins/security_solution/public/users/pages/users.tsx b/x-pack/plugins/security_solution/public/users/pages/users.tsx index 9d567e7605074..1f87ec55f808d 100644 --- a/x-pack/plugins/security_solution/public/users/pages/users.tsx +++ b/x-pack/plugins/security_solution/public/users/pages/users.tsx @@ -102,7 +102,7 @@ const UsersComponent = () => { return filters; }, [severitySelection, tabName, filters]); - const { docValueFields, indicesExist, indexPattern, selectedPatterns } = useSourcererDataView(); + const { indicesExist, indexPattern, selectedPatterns } = useSourcererDataView(); const [filterQuery, kqlError] = useMemo( () => convertToBuildEsQuery({ @@ -210,7 +210,6 @@ const UsersComponent = () => { String(id) !== ''); +} + +/** + * Returns the resolver fields filter to use in queries to limit the number of fields returned in the + * query response. * @param schema is the node schema information describing how relationships are formed between nodes * in the resolver graph. */ -export function docValueFields(schema: ResolverSchema): Array<{ field: string }> { +export function resolverFields(schema: ResolverSchema): Array<{ field: string }> { const filter = [{ field: '@timestamp' }, { field: schema.id }, { field: schema.parent }]; if (schema.ancestry) { filter.push({ field: schema.ancestry }); @@ -40,12 +45,3 @@ export function docValueFields(schema: ResolverSchema): Array<{ field: string }> } return filter; } - -/** - * Returns valid IDs that can be used in a search. - * - * @param ids array of ids - */ -export function validIDs(ids: NodeID[]): NodeID[] { - return ids.filter((id) => String(id) !== ''); -} diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/query.dns_histogram.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/query.dns_histogram.dsl.ts index 17f083eeca9c1..c547f0a6ada3f 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/query.dns_histogram.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/query.dns_histogram.dsl.ts @@ -5,8 +5,6 @@ * 2.0. */ -import { isEmpty } from 'lodash/fp'; - import moment from 'moment'; import type { MatrixHistogramRequestOptions } from '../../../../../../common/search_strategy'; @@ -57,7 +55,6 @@ const getHistogramAggregation = ({ from, to }: { from: string; to: string }) => export const buildDnsHistogramQuery = ({ defaultIndex, - docValueFields, filterQuery, isPtrIncluded = false, stackByField = 'dns.question.registered_domain', @@ -81,7 +78,6 @@ export const buildDnsHistogramQuery = ({ index: defaultIndex, ignore_unavailable: true, body: { - ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), aggregations: { ...getCountAgg(), dns_name_query_count: { diff --git a/x-pack/plugins/session_view/common/constants.ts b/x-pack/plugins/session_view/common/constants.ts index 5bf0dc53701fc..538605bb591b0 100644 --- a/x-pack/plugins/session_view/common/constants.ts +++ b/x-pack/plugins/session_view/common/constants.ts @@ -43,7 +43,15 @@ export const ALERT_STATUS = { export const LOCAL_STORAGE_DISPLAY_OPTIONS_KEY = 'sessionView:displayOptions'; export const MOUSE_EVENT_PLACEHOLDER = { stopPropagation: () => undefined } as React.MouseEvent; export const DEBOUNCE_TIMEOUT = 500; -export const DEFAULT_TTY_PLAYSPEED_MS = 40; // milli seconds per line of tty output. +export const DEFAULT_TTY_PLAYSPEED_MS = 80; // milli seconds per line of tty output. +export const DEFAULT_TTY_FONT_SIZE = 11; + +// we split terminal output on both newlines and cursor movements. +export const TTY_LINE_SPLITTER_REGEX = /(\r?\n|\x1b\[\d+;\d+[Hf])/gi; + +// used when searching output +export const TTY_STRIP_CONTROL_CODES_REGEX = + /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/gi; // when showing the count of alerts in details panel tab, if the number // exceeds ALERT_COUNT_THRESHOLD we put a + next to it, e.g 999+ diff --git a/x-pack/plugins/session_view/common/mocks/responses/session_view_io_events.mock.ts b/x-pack/plugins/session_view/common/mocks/responses/session_view_io_events.mock.ts index 33b9ac56b035d..c14dccc2bc3d6 100644 --- a/x-pack/plugins/session_view/common/mocks/responses/session_view_io_events.mock.ts +++ b/x-pack/plugins/session_view/common/mocks/responses/session_view_io_events.mock.ts @@ -17,6 +17,7 @@ export const sessionViewIOEventsMock: ProcessEventResults = { message: 'hello world security', event: { action: 'text_output', + id: '1', }, process: { entity_id: '1', @@ -30,7 +31,15 @@ export const sessionViewIOEventsMock: ProcessEventResults = { total_bytes_captured: 1024, total_bytes_skipped: 0, bytes_skipped: [], - text: "256\n,\n Some Companies Puppet instance\n | | | CentOS Stream release 8 on x86_64\n .=/ = = =| =| = === = Load average: 1.23, 1.01, 0.63\n | || || || || || | | | | \n /= = = =' =' =' ' =' Hostname ********\n \\ Type xyz\n o Datacenter ********\n Cluster ********\n\n\n\n\n,0 loaded units listed. Pass --all to see loaded but inactive units, too.\nTo show all installed unit files use 'systemctl list-unit-files'.\n", + text: "256\n,\n Some Companies Puppet instance\n | | | CentOS Stream release 8 on x86_64\n *********************** Load average: 1.23, 1.01, 0.63\n ************************ \n ************************ Hostname ********\n \\ Type xyz\n o Datacenter ********\n Cluster ********\n\n\n\n\n,0 loaded units listed. Pass --all to see loaded but inactive units, too.\nTo show all installed unit files use 'systemctl list-unit-files'.\n", + }, + tty: { + char_device: { + major: 4, + minor: 1, + }, + rows: 59, + columns: 173, }, }, }, @@ -44,6 +53,7 @@ export const sessionViewIOEventsMock: ProcessEventResults = { message: 'hello world security', event: { action: 'text_output', + id: '1', }, process: { entity_id: '2', @@ -57,7 +67,15 @@ export const sessionViewIOEventsMock: ProcessEventResults = { total_bytes_captured: 1024, total_bytes_skipped: 0, bytes_skipped: [], - text: ',\u001b[?2004h\u001b[?1049h\u001b[22;0;0t\u001b[?1h\u001b=\u001b[?2004h\u001b[1;59r\u001b[?12h\u001b[?12l\u001b[27m\u001b[29m\u001b[m\u001b[H\u001b[2J\u001b[?25l\u001b[59;1H"/usr/local/bin/galera_traffic_start.sh" [readonly] 14L, 397C\u001b[1;1H#!/bin/env bash\n# Copyright (C) 2022, ********(R) Corporation. All rights reserved.\n\n# Script for setting the reject of queries in Galera\n\nmysql -h127.0.0.1 -P6033 -uroot -e "set global wsrep_reject_queries=\'NONE\'" 2>&1\nRC=$?\n\nif [[ $RC != 0 ]]; then\n >&2 echo "Failed to unset the reject of queries on Galera node, exiting."\n exit $RC\nelse\n echo "Successfully unset the reject of queries."\nfi\n\u001b[94m~ \u001b[16;1H~ \u001b[17;1H~ \u001b[18;1H~ \u001b[19;1H~ \u001b[20;1H~ \u001b[21;1H~ \u001b[22;1H~ \u001b[23;1H~ \u001b[24;1H~ \u001b[25;1H~ \u001b[26;1H~ \u001b[27;1H~ \u001b[28;1H~ \u001b[29;1H~ \u001b[30;1H~ \u001b[31;1H~ \u001b[32;1H~ \u001b[33;1H~ \u001b[34;1H~ \u001b[35;1H~ \u001b[36;1H~ \u001b[37;1H~ \u001b[38;1H~ \u001b[39;1H~ \u001b[40;1H~ \u001b[41;1H~ \u001b[42;1H~ \u001b[43;1H~ \u001b[44;1H~ \u001b[45;1H~ \u001b[46;1H~ \u001b[47;1H~ \u001b[48;1H~ \u001b[49;1H~ \u001b[50;1H~ \u001b[51;1H~ \u001b[52;1H~ \u001b[53;1H~ \u001b[54;1H~ \u001b[55;1H~ \u001b[56;1H~ \u001b[57;1H~ \u001b[58;1H~ \u001b[1;1H\u001b[?25h\u0007\u001b[?25l\u001b[m\u001b[59;1H\u001b[K\u001b[59;1H:\u001b[?2004h\u001b[?25hq\r\u001b[?25l\u001b[?2004l\u001b[59;1H\u001b[K\u001b[59;1H\u001b[?2004l\u001b[?1l\u001b>\u001b[?25h\u001b[?1049l\u001b[23;0;0t,\u001b[?2004h\u001b[?1049h\u001b[22;0;0t\u001b[?1h\u001b=\u001b[?2004h\u001b[1;59r\u001b[?12h\u001b[?12l\u001b[27m\u001b[29m\u001b[m\u001b[H\u001b[2J\u001b[?25l\u001b[59;1H"/usr/local/bin/galera_traffic_stop.sh" [readonly] 115L, 3570C\u001b[1;1H#!/bin/env bash\n# Copyright (C) 2022, ********(R) Corporation. All rights reserved.\n\n# Script for rejecting connection on Galera cluster node, either gracefully or not,\n# depending on supplied arguments.\n\nfunction usage() {\n echo "\n This script disables DB connections to Galera node.\n The default is to stop them gracefully.\n\n Usage: $0 [-h] [-w ] [-s ] [-x]\n\n Options:\n -h Prints this help.\n -w Number of seconds for waiting to close the connections.\u001b[17;11HDefault value is to wait for mysql-wait_timeout.\n -s Sleep interval between connections checks.\n -x Kills all connections immediately. Other options are ignored."\n exit\n}\n', + text: ',\u001b[?2004h\u001b[?1049h\u001b[22;0;0t\u001b[?1h\u001b=\u001b[?2004h\u001b[1;59r\u001b[?12h\u001b[?12l\u001b[27m\u001b[29m\u001b[m\u001b[H\u001b[2J\u001b[?25l\u001b[59;1H"/usr/local/bin/script_one.sh" [readonly] 14L, 397C\u001b[1;1H#!/bin/env bash\n# Copyright (C) 2022, ********(R) Corporation. All rights reserved.\n\n# Script for setting the reject of queries in Mysql\n\nmysql -h127.0.0.1 -P6033 -uroot -e "set global wsrep_reject_queries=\'NONE\'" 2>&1\nRC=$?\n\nif [[ $RC != 0 ]]; then\n >&2 echo "Failed to unset the reject of queries on Mysql node, exiting."\n exit $RC\nelse\n echo "Successfully unset the reject of queries."\nfi\n\u001b[94m~ \u001b[16;1H~ \u001b[17;1H~ \u001b[18;1H~ \u001b[19;1H~ \u001b[20;1H~ \u001b[21;1H~ \u001b[22;1H~ \u001b[23;1H~ \u001b[24;1H~ \u001b[25;1H~ \u001b[26;1H~ \u001b[27;1H~ \u001b[28;1H~ \u001b[29;1H~ \u001b[30;1H~ \u001b[31;1H~ \u001b[32;1H~ \u001b[33;1H~ \u001b[34;1H~ \u001b[35;1H~ \u001b[36;1H~ \u001b[37;1H~ \u001b[38;1H~ \u001b[39;1H~ \u001b[40;1H~ \u001b[41;1H~ \u001b[42;1H~ \u001b[43;1H~ \u001b[44;1H~ \u001b[45;1H~ \u001b[46;1H~ \u001b[47;1H~ \u001b[48;1H~ \u001b[49;1H~ \u001b[50;1H~ \u001b[51;1H~ \u001b[52;1H~ \u001b[53;1H~ \u001b[54;1H~ \u001b[55;1H~ \u001b[56;1H~ \u001b[57;1H~ \u001b[58;1H~ \u001b[1;1H\u001b[?25h\u0007\u001b[?25l\u001b[m\u001b[59;1H\u001b[K\u001b[59;1H:\u001b[?2004h\u001b[?25hq\r\u001b[?25l\u001b[?2004l\u001b[59;1H\u001b[K\u001b[59;1H\u001b[?2004l\u001b[?1l\u001b>\u001b[?25h\u001b[?1049l\u001b[23;0;0t,\u001b[?2004h\u001b[?1049h\u001b[22;0;0t\u001b[?1h\u001b=\u001b[?2004h\u001b[1;59r\u001b[?12h\u001b[?12l\u001b[27m\u001b[29m\u001b[m\u001b[H\u001b[2J\u001b[?25l\u001b[59;1H"/usr/local/bin/script_two.sh" [readonly] 115L, 3570C\u001b[1;1H#!/bin/env bash\n# Copyright (C) 2022, ********(R) Corporation. All rights reserved.\n\n# Script for rejecting connection on Mysql cluster node, either gracefully or not,\n# depending on supplied arguments.\n\nfunction usage() {\n echo "\n This script disables DB connections to Mysql node.\n The default is to stop them gracefully.\n\n Usage: $0 [-h] [-w ] [-s ] [-x]\n\n Options:\n -h Prints this help.\n -w Number of seconds for waiting to close the connections.\u001b[17;11HDefault value is to wait for mysql-wait_timeout.\n -s Sleep interval between connections checks.\n -x Kills all connections immediately. Other options are ignored."\n exit\n}\n', + }, + tty: { + char_device: { + major: 4, + minor: 1, + }, + rows: 59, + columns: 173, }, }, }, @@ -71,6 +89,7 @@ export const sessionViewIOEventsMock: ProcessEventResults = { message: 'hello world security', event: { action: 'text_output', + id: '1', }, process: { entity_id: '2', @@ -84,7 +103,15 @@ export const sessionViewIOEventsMock: ProcessEventResults = { total_bytes_captured: 1024, total_bytes_skipped: 0, bytes_skipped: [], - text: '\nfunction get_number_db_connections() {\n # count current\n DB_CONNECTIONS_NUMBER=$(mysql -h127.0.0.1 -P6032 -uadmin -N --silent -e "select count(1) from stats_mysql_processlist where user = \'$DB_USER\' and db like \'db\\_%\' escapee\u001b[26;1H \'\\\'")\n}\n\nfunction set_number_grace_seconds() {\n local mysql_wait_timeout_ms=$(mysql -h127.0.0.1 -P6032 -uadmin -N --silent -e "select variable_value from global_variables where variable_name = \'mysql-wait_timeout\'")\n GRACE_PERIOD=$((($mysql_wait_timeout_ms+1000-1)/1000))\n}\n\nfunction wait_for_connections() {\n local number_of_loops=$(((($GRACE_PERIOD+$SLEEP_INTERVAL-1)/$SLEEP_INTERVAL)))\u001b[37;5Hecho "Waiting for connections to close for up to $GRACE_PERIOD seconds"\u001b[39;5Hfor i in $(seq 0 $number_of_loops); do\u001b[40;9Hget_number_db_connections\u001b[41;9Hif [[ $DB_CONNECTIONS_NUMBER -eq 0 ]]; then\u001b[42;13Hecho "No connection found for user $DB_USER to this node"\u001b[43;13Hbreak\u001b[44;9Helse\u001b[45;13Hecho "$DB_CONNECTIONS_NUMBER connection(s) found, waiting for ${SLEEP_INTERVAL}s, round $i"\u001b[46;13Hsleep $SLEEP_INTERVAL\u001b[47;9Hfi\n done\n}\n\nfunction parse_args() {\n while getopts \'hs:w:x\' opt; do\u001b[53;9Hcase "$opt" in\u001b[54;9Hh)\u001b[55;13Husage\u001b[56;13H;;\u001b[57;9Hs)\u001b[58;13Hif ! [[ $OPTARG =~ ^[0-9]+$ ]]; then\u001b[1;1H\u001b[?25h\u001b[?25l\u001b[59;1H\u001b[K\u001b[59;1H:\u001b[?2004h\u001b[?25hset number\r\u001b[?25l\u001b[1;1H\u001b[38;5;130m 1 \u001b[m#!/bin/env bash\n\u001b[38;5;130m 2 \u001b[m# Copyright (C) 2022, ********(R) Corporation. All rights reserved.\n\u001b[38;5;130m 3 \n 4 \u001b[m# Script for rejecting connection on Galera cluster node, either gracefully or not,\n\u001b[38;5;130m 5 \u001b[m# depending on supplied arguments.\n\u001b[38;5;130m 6 \n 7 \u001b[mfunction usage() {\n\u001b[38;5;130m 8 \u001b[m echo "\n\u001b[38;5;130m 9 \u001b[m This script disables DB connections to Galera node.\n\u001b[38;5;130m 10 \u001b[m The default is to stop them gracefully.\n\u001b[38;5;130m 11 \n 12 \u001b[m Usage: $0 [-h] [-w ] [-s ] [-x]\n\u001b[38;5;130m 13 \n 14 \u001b[m Options:\n\u001b[38;5;130m 15 \u001b[m -h Prints this help.\n\u001b[38;5;130m 16 \u001b[m -w Number of seconds for waiting to close the connections.\n\u001b[38;5;130m 17 \u001b[m Default value is to wait for mysql-wait_timeout.\n\u001b[38;5;130m 18 \u001b[m -s Sleep interval between connections checks.\n\u001b[38;5;130m 19 \u001b[m -x Kills all connections immediately. Other options are ignored."\n\u001b[38;5;130m 20 \u001b[m exit\n\u001b[38;5;130m 21 \u001b[m}\n\u001b[38;5;130m 22 \n 23 \u001b[mfunction get_number_db_connections() {\n\u001b[38;5;130m 24 \u001b[m # count current\n\u001b[38;5;130m 25 \u001b[m DB_CONNECTIONS_NUMBER=$(mysql -h127.0.0.1 -P6032 -uadmin -N --silent -e "select count(1) from stats_mysql_processlist where user = \'$DB_USER\' and db like \'db\\_%%\u001b[26;1H\u001b[38;5;130m \u001b[m\' escape \'\\\'")\n\u001b[38;5;130m 26 \u001b[m}\n\u001b[38;5;130m 27 \n 28 \u001b[mfunction set_number_grace_seconds() {\n\u001b[38;5;130m 29 \u001b[m local mysql_wait_timeout_ms=$(mysql -h127.0.0.1 -P6032 -uadmin -N --silent -e "select variable_value from global_variables where variable_name = \'mysql-wait_timm\u001b[31;1H\u001b[38;5;130m \u001b[meout\'")\u001b[31;16H\u001b[K\u001b[32;1H\u001b[38;5;130m 30 \u001b[m GRACE_PERIOD=$((($mysql_wait_timeout_ms+1000-1)/1000))\n\u001b[38;5;130m 31 \u001b[m}\n\u001b[38;5;130m 32 \u001b[m\u001b[34;10H\u001b[K\u001b[35;1H\u001b[38;5;130m 33 \u001b[mfunction wait_for_connections() {\u001b[35;42H\u001b[K\u001b[36;1H\u001b[38;5;130m 34 \u001b[m local number_of_loops=$(((($GRACE_PERIOD+$SLEEP_INTERVAL-1)/$SLEEP_INTERVAL)))\n\u001b[38;5;130m 35 \u001b[m\u001b[37;10H\u001b[K\u001b[38;1H\u001b[38;5;130m 36 \u001b[m echo "Waiting for connections to close for up to $GRACE_PERIOD seconds"\n\u001b[38;5;130m 37 \u001b[m\u001b[39;9H\u001b[K\u001b[40;1H\u001b[38;5;130m 38 \u001b[m for i in $(seq 0 $number_of_loops); do\n', + text: '\nfunction get_number_db_connections() {\n # count current\n DB_CONNECTIONS_NUMBER=$(mysql -h127.0.0.1 -P6032 -uadmin -N --silent -e "select count(1) from stats_mysql_processlist where user = \'$DB_USER\' and db like \'db\\_%\' escapee\u001b[26;1H \'\\\'")\n}\n\nfunction set_number_grace_seconds() {\n local mysql_wait_timeout_ms=$(mysql -h127.0.0.1 -P6032 -uadmin -N --silent -e "select variable_value from global_variables where variable_name = \'mysql-wait_timeout\'")\n GRACE_PERIOD=$((($mysql_wait_timeout_ms+1000-1)/1000))\n}\n\nfunction wait_for_connections() {\n local number_of_loops=$(((($GRACE_PERIOD+$SLEEP_INTERVAL-1)/$SLEEP_INTERVAL)))\u001b[37;5Hecho "Waiting for connections to close for up to $GRACE_PERIOD seconds"\u001b[39;5Hfor i in $(seq 0 $number_of_loops); do\u001b[40;9Hget_number_db_connections\u001b[41;9Hif [[ $DB_CONNECTIONS_NUMBER -eq 0 ]]; then\u001b[42;13Hecho "No connection found for user $DB_USER to this node"\u001b[43;13Hbreak\u001b[44;9Helse\u001b[45;13Hecho "$DB_CONNECTIONS_NUMBER connection(s) found, waiting for ${SLEEP_INTERVAL}s, round $i"\u001b[46;13Hsleep $SLEEP_INTERVAL\u001b[47;9Hfi\n done\n}\n\nfunction parse_args() {\n while getopts \'hs:w:x\' opt; do\u001b[53;9Hcase "$opt" in\u001b[54;9Hh)\u001b[55;13Husage\u001b[56;13H;;\u001b[57;9Hs)\u001b[58;13Hif ! [[ $OPTARG =~ ^[0-9]+$ ]]; then\u001b[1;1H\u001b[?25h\u001b[?25l\u001b[59;1H\u001b[K\u001b[59;1H:\u001b[?2004h\u001b[?25hset number\r\u001b[?25l\u001b[1;1H\u001b[38;5;130m 1 \u001b[m#!/bin/env bash\n\u001b[38;5;130m 2 \u001b[m# Copyright (C) 2022, ********(R) Corporation. All rights reserved.\n\u001b[38;5;130m 3 \n 4 \u001b[m# Script for rejecting connection on Mysql cluster node, either gracefully or not,\n\u001b[38;5;130m 5 \u001b[m# depending on supplied arguments.\n\u001b[38;5;130m 6 \n 7 \u001b[mfunction usage() {\n\u001b[38;5;130m 8 \u001b[m echo "\n\u001b[38;5;130m 9 \u001b[m This script disables DB connections to Mysql node.\n\u001b[38;5;130m 10 \u001b[m The default is to stop them gracefully.\n\u001b[38;5;130m 11 \n 12 \u001b[m Usage: $0 [-h] [-w ] [-s ] [-x]\n\u001b[38;5;130m 13 \n 14 \u001b[m Options:\n\u001b[38;5;130m 15 \u001b[m -h Prints this help.\n\u001b[38;5;130m 16 \u001b[m -w Number of seconds for waiting to close the connections.\n\u001b[38;5;130m 17 \u001b[m Default value is to wait for mysql-wait_timeout.\n\u001b[38;5;130m 18 \u001b[m -s Sleep interval between connections checks.\n\u001b[38;5;130m 19 \u001b[m -x Kills all connections immediately. Other options are ignored."\n\u001b[38;5;130m 20 \u001b[m exit\n\u001b[38;5;130m 21 \u001b[m}\n\u001b[38;5;130m 22 \n 23 \u001b[mfunction get_number_db_connections() {\n\u001b[38;5;130m 24 \u001b[m # count current\n\u001b[38;5;130m 25 \u001b[m DB_CONNECTIONS_NUMBER=$(mysql -h127.0.0.1 -P6032 -uadmin -N --silent -e "select count(1) from stats_mysql_processlist where user = \'$DB_USER\' and db like \'db\\_%%\u001b[26;1H\u001b[38;5;130m \u001b[m\' escape \'\\\'")\n\u001b[38;5;130m 26 \u001b[m}\n\u001b[38;5;130m 27 \n 28 \u001b[mfunction set_number_grace_seconds() {\n\u001b[38;5;130m 29 \u001b[m local mysql_wait_timeout_ms=$(mysql -h127.0.0.1 -P6032 -uadmin -N --silent -e "select variable_value from global_variables where variable_name = \'mysql-wait_timm\u001b[31;1H\u001b[38;5;130m \u001b[meout\'")\u001b[31;16H\u001b[K\u001b[32;1H\u001b[38;5;130m 30 \u001b[m GRACE_PERIOD=$((($mysql_wait_timeout_ms+1000-1)/1000))\n\u001b[38;5;130m 31 \u001b[m}\n\u001b[38;5;130m 32 \u001b[m\u001b[34;10H\u001b[K\u001b[35;1H\u001b[38;5;130m 33 \u001b[mfunction wait_for_connections() {\u001b[35;42H\u001b[K\u001b[36;1H\u001b[38;5;130m 34 \u001b[m local number_of_loops=$(((($GRACE_PERIOD+$SLEEP_INTERVAL-1)/$SLEEP_INTERVAL)))\n\u001b[38;5;130m 35 \u001b[m\u001b[37;10H\u001b[K\u001b[38;1H\u001b[38;5;130m 36 \u001b[m echo "Waiting for connections to close for up to $GRACE_PERIOD seconds"\n\u001b[38;5;130m 37 \u001b[m\u001b[39;9H\u001b[K\u001b[40;1H\u001b[38;5;130m 38 \u001b[m for i in $(seq 0 $number_of_loops); do\n', + }, + tty: { + char_device: { + major: 4, + minor: 1, + }, + rows: 59, + columns: 173, }, }, }, @@ -98,6 +125,7 @@ export const sessionViewIOEventsMock: ProcessEventResults = { message: 'hello world security', event: { action: 'text_output', + id: '1', }, process: { entity_id: '2', @@ -113,6 +141,14 @@ export const sessionViewIOEventsMock: ProcessEventResults = { bytes_skipped: [], text: '\u001b[38;5;130m 39 \u001b[m get_number_db_connections\u001b[41;42H\u001b[K\u001b[42;1H\u001b[38;5;130m 40 \u001b[m if [[ $DB_CONNECTIONS_NUMBER -eq 0 ]]; then\u001b[42;60H\u001b[K\u001b[43;1H\u001b[38;5;130m 41 \u001b[m echo "No connection found for user $DB_USER to this node"\n\u001b[38;5;130m 42 \u001b[m \u001b[8Cbreak\n\u001b[38;5;130m 43 \u001b[m else\u001b[45;21H\u001b[K\u001b[46;1H\u001b[38;5;130m 44 \u001b[m echo "$DB_CONNECTIONS_NUMBER connection(s) found, waiting for ${SLEEP_INTERVAL}s, round $i"\n\u001b[38;5;130m 45 \u001b[m \u001b[10Csleep $SLEEP_INTERVAL\n\u001b[38;5;130m 46 \u001b[m\u001b[8Cfi\n\u001b[38;5;130m 47 \u001b[m done\n\u001b[38;5;130m 48 \u001b[m}\n\u001b[38;5;130m 49 \u001b[m\u001b[51;10H\u001b[K\u001b[52;1H\u001b[38;5;130m 50 \u001b[mfunction parse_args() {\u001b[52;33H\u001b[K\u001b[53;1H\u001b[38;5;130m 51 \u001b[m while getopts \'hs:w:x\' opt; do\n\u001b[38;5;130m 52 \u001b[m case "$opt" in\n\u001b[38;5;130m 53 \u001b[m h)\n\u001b[38;5;130m 54 \u001b[m usage\n\u001b[38;5;130m 55 \u001b[m \u001b[10C;;\n\u001b[38;5;130m 56 \u001b[m s)\u001b[58;19H\u001b[K\u001b[1;9H\u001b[?25h\u001b[?25l\u001b[27m\u001b[29m\u001b[m\u001b[H\u001b[2J\u001b[1;1H\u001b[38;5;130m 58 \u001b[m\u001b[16C>&2 echo "Sleep interval (-s) must be a number"\n\u001b[38;5;130m 59 \u001b[m\u001b[16Cexit 1\n\u001b[38;5;130m 60 \u001b[m\u001b[12Cfi\n\u001b[38;5;130m 61 \u001b[m\u001b[12CARG_SLEEP_INTERVAL="$OPTARG"\n\u001b[38;5;130m 62 \u001b[m\u001b[12C;;\n\u001b[38;5;130m 63 \u001b[m\u001b[8Cw)\n\u001b[38;5;130m 64 \u001b[m\u001b[12Cif ! [[ $OPTARG =~ ^[0-9]+$ ]]; then\n\u001b[38;5;130m 65 \u001b[m\u001b[16C>&2 echo "Wait timeout (-w) must be a number"\n\u001b[38;5;130m 66 \u001b[m\u001b[16Cexit 1\n\u001b[38;5;130m 67 \u001b[m\u001b[12Cfi\n\u001b[38;5;130m 68 \u001b[m\u001b[12CARG_GRACE_PERIOD="$OPTARG"\n\u001b[38;5;130m 69 \u001b[m\u001b[12C;;\n\u001b[38;5;130m 70 \u001b[m\u001b[8Cx)\n\u001b[38;5;130m 71 \u001b[m\u001b[12CARG_KILL_IMMEDIATELY=1\n\u001b[38;5;130m 72 \u001b[m\u001b[12C;;\n\u001b[38;5;130m 73 \u001b[m\u001b[8Cesac\n\u001b[38;5;130m 74 \u001b[m done\n\u001b[38;5;130m 75 \n 76 \u001b[m GRACE_PERIOD=${ARG_GRACE_PERIOD:--1}\n\u001b[38;5;130m 77 \u001b[m SLEEP_INTERVAL=${ARG_SLEEP_INTERVAL:-30}\n\u001b[38;5;130m 78 \u001b[m KILL_IMMEDIATELY=${ARG_KILL_IMMEDIATELY:-0}\n\u001b[38;5;130m 79 \u001b[m}\n\u001b[38;5;130m 80 \n 81 \u001b[mDB_USER="rolap01"\n\u001b[38;5;130m 82 \n 83 \u001b[mparse_args $@\n\u001b[38;5;130m 84 \n 85 \u001b[mif [[ $KILL_IMMEDIATELY == 1 ]]; then\n\u001b[38;5;130m 86 \u001b[m echo "WARNING: Not waiting for connections to close gracefully"\n\u001b[38;5;130m 87 \u001b[m echo "Press any key to continue... wsrep_reject_queries will be set to \'ALL_KILL\'"\n\u001b[38;5;130m 88 \u001b[m read a\n\u001b[38;5;130m 89 \u001b[m mysql -h127.0.0.1 -P3306 -uroot -e "set global wsrep_reject_queries=\'ALL_KILL\'"\n\u001b[38;5;130m 90 \u001b[melse\n\u001b[38;5;130m 91 \u001b[m # Stop accepting queries in mariadb, do not kill opened connections\n\u001b[38;5;130m 92 \u001b[m mysql -h127.0.0.1 -P3306 -uroot -e "set global wsrep_reject_queries=\'ALL\'"\n\u001b[38;5;130m 93 \u001b[mfi\n\u001b[38;5;130m 94 \n 95 \u001b[mexit_code=$?\n', }, + tty: { + char_device: { + major: 4, + minor: 1, + }, + rows: 59, + columns: 173, + }, }, }, }, @@ -125,6 +161,7 @@ export const sessionViewIOEventsMock: ProcessEventResults = { message: 'hello world security', event: { action: 'text_output', + id: '1', }, process: { entity_id: '2', @@ -138,7 +175,15 @@ export const sessionViewIOEventsMock: ProcessEventResults = { total_bytes_captured: 1024, total_bytes_skipped: 0, bytes_skipped: [], - text: '\u001b[38;5;130m 96 \u001b[mif [[ $exit_code != 0 ]]; then\n\u001b[38;5;130m 97 \u001b[m >&2 echo "Failed to set the reject of queries on Galera node, exiting."\n\u001b[38;5;130m 98 \u001b[m exit $exit_code\n\u001b[38;5;130m 99 \u001b[melse\n\u001b[38;5;130m 100 \u001b[m echo "Successfully stopped accepting queries."\n\u001b[38;5;130m 101 \u001b[m if [[ $KILL_IMMEDIATELY == 1 ]]; then\n\u001b[38;5;130m 102 \u001b[m\u001b[8Cexit\n\u001b[38;5;130m 103 \u001b[m fi\n\u001b[38;5;130m 104 \u001b[mfi\n\u001b[38;5;130m 105 \n 106 \u001b[mif [[ $GRACE_PERIOD == -1 ]]; then\n\u001b[38;5;130m 107 \u001b[m set_number_grace_seconds\n\u001b[38;5;130m 108 \u001b[mfi\n\u001b[38;5;130m 109 \n 110 \u001b[mwait_for_connections\n\u001b[38;5;130m 111 \u001b[mif [[ $DB_CONNECTIONS_NUMBER != 0 ]]; then\n\u001b[38;5;130m 112 \u001b[m get_number_db_connections\n\u001b[38;5;130m 113 \u001b[m >&2 echo "ERROR: There are still $DB_CONNECTIONS_NUMBER opened DB connections."\n\u001b[38;5;130m 114 \u001b[m exit 3\n\u001b[38;5;130m 115 \u001b[mfi\b\b\u001b[?25h\u001b[?25l\u001b[27m\u001b[29m\u001b[m\u001b[H\u001b[2J\u001b[1;1H\u001b[38;5;130m 1 \u001b[m#!/bin/env bash\n\u001b[38;5;130m 2 \u001b[m# Copyright (C) 2022, ********(R) Corporation. All rights reserved.\n\u001b[38;5;130m 3 \n 4 \u001b[m# Script for rejecting connection on Galera cluster node, either gracefully or not,\n\u001b[38;5;130m 5 \u001b[m# depending on supplied arguments.\n\u001b[38;5;130m 6 \n 7 \u001b[mfunction usage() {\n\u001b[38;5;130m 8 \u001b[m echo "\n\u001b[38;5;130m 9 \u001b[m This script disables DB connections to Galera node.\n\u001b[38;5;130m 10 \u001b[m The default is to stop them gracefully.\n\u001b[38;5;130m 11 \n 12 \u001b[m Usage: $0 [-h] [-w ] [-s ] [-x]\n\u001b[38;5;130m 13 \n 14 \u001b[m Options:\n\u001b[38;5;130m 15 \u001b[m -h Prints this help.\n\u001b[38;5;130m 16 \u001b[m -w Number of seconds for waiting to close the connections.\n\u001b[38;5;130m 17 \u001b[m\u001b[10CDefault value is to wait for mysql-wait_timeout.\n\u001b[38;5;130m 18 \u001b[m -s Sleep interval between connections checks.\n\u001b[38;5;130m 19 \u001b[m -x Kills all connections immediately. Other options are ignored."\n\u001b[38;5;130m 20 \u001b[m exit\n\u001b[38;5;130m 21 \u001b[m}\n\u001b[38;5;130m 22 \n 23 \u001b[mfunction get_number_db_connections() {\n\u001b[38;5;130m 24 \u001b[m # count current\n\u001b[38;5;130m 25 \u001b[m DB_CONNECTIONS_NUMBER=$(mysql -h127.0.0.1 -P6032 -uadmin -N --silent -e "select count(1) from stats_mysql_processlist where user = \'$DB_USER\' and db like \'db\\_%%\u001b[26;1H\u001b[38;5;130m \u001b[m\' escape \'\\\'")\n\u001b[38;5;130m 26 \u001b[m}\n\u001b[38;5;130m 27 \n 28 \u001b[mfunction set_number_grace_seconds() {\n\u001b[38;5;130m 29 \u001b[m local mysql_wait_timeout_ms=$(mysql -h127.0.0.1 -P6032 -uadmin -N --silent -e "select variable_value from global_variables where variable_name = \'mysql-wait_timm\u001b[31;1H\u001b[38;5;130m \u001b[meout\'")\n\u001b[38;5;130m 30 \u001b[m GRACE_PERIOD=$((($mysql_wait_timeout_ms+1000-1)/1000))\n\u001b[38;5;130m 31 \u001b[m}\n\u001b[38;5;130m 32 \n', + text: '\u001b[38;5;130m 96 \u001b[mif [[ $exit_code != 0 ]]; then\n\u001b[38;5;130m 97 \u001b[m >&2 echo "Failed to set the reject of queries on Mysql node, exiting."\n\u001b[38;5;130m 98 \u001b[m exit $exit_code\n\u001b[38;5;130m 99 \u001b[melse\n\u001b[38;5;130m 100 \u001b[m echo "Successfully stopped accepting queries."\n\u001b[38;5;130m 101 \u001b[m if [[ $KILL_IMMEDIATELY == 1 ]]; then\n\u001b[38;5;130m 102 \u001b[m\u001b[8Cexit\n\u001b[38;5;130m 103 \u001b[m fi\n\u001b[38;5;130m 104 \u001b[mfi\n\u001b[38;5;130m 105 \n 106 \u001b[mif [[ $GRACE_PERIOD == -1 ]]; then\n\u001b[38;5;130m 107 \u001b[m set_number_grace_seconds\n\u001b[38;5;130m 108 \u001b[mfi\n\u001b[38;5;130m 109 \n 110 \u001b[mwait_for_connections\n\u001b[38;5;130m 111 \u001b[mif [[ $DB_CONNECTIONS_NUMBER != 0 ]]; then\n\u001b[38;5;130m 112 \u001b[m get_number_db_connections\n\u001b[38;5;130m 113 \u001b[m >&2 echo "ERROR: There are still $DB_CONNECTIONS_NUMBER opened DB connections."\n\u001b[38;5;130m 114 \u001b[m exit 3\n\u001b[38;5;130m 115 \u001b[mfi\b\b\u001b[?25h\u001b[?25l\u001b[27m\u001b[29m\u001b[m\u001b[H\u001b[2J\u001b[1;1H\u001b[38;5;130m 1 \u001b[m#!/bin/env bash\n\u001b[38;5;130m 2 \u001b[m# Copyright (C) 2022, ********(R) Corporation. All rights reserved.\n\u001b[38;5;130m 3 \n 4 \u001b[m# Script for rejecting connection on Mysql cluster node, either gracefully or not,\n\u001b[38;5;130m 5 \u001b[m# depending on supplied arguments.\n\u001b[38;5;130m 6 \n 7 \u001b[mfunction usage() {\n\u001b[38;5;130m 8 \u001b[m echo "\n\u001b[38;5;130m 9 \u001b[m This script disables DB connections to Mysql node.\n\u001b[38;5;130m 10 \u001b[m The default is to stop them gracefully.\n\u001b[38;5;130m 11 \n 12 \u001b[m Usage: $0 [-h] [-w ] [-s ] [-x]\n\u001b[38;5;130m 13 \n 14 \u001b[m Options:\n\u001b[38;5;130m 15 \u001b[m -h Prints this help.\n\u001b[38;5;130m 16 \u001b[m -w Number of seconds for waiting to close the connections.\n\u001b[38;5;130m 17 \u001b[m\u001b[10CDefault value is to wait for mysql-wait_timeout.\n\u001b[38;5;130m 18 \u001b[m -s Sleep interval between connections checks.\n\u001b[38;5;130m 19 \u001b[m -x Kills all connections immediately. Other options are ignored."\n\u001b[38;5;130m 20 \u001b[m exit\n\u001b[38;5;130m 21 \u001b[m}\n\u001b[38;5;130m 22 \n 23 \u001b[mfunction get_number_db_connections() {\n\u001b[38;5;130m 24 \u001b[m # count current\n\u001b[38;5;130m 25 \u001b[m DB_CONNECTIONS_NUMBER=$(mysql -h127.0.0.1 -P6032 -uadmin -N --silent -e "select count(1) from stats_mysql_processlist where user = \'$DB_USER\' and db like \'db\\_%%\u001b[26;1H\u001b[38;5;130m \u001b[m\' escape \'\\\'")\n\u001b[38;5;130m 26 \u001b[m}\n\u001b[38;5;130m 27 \n 28 \u001b[mfunction set_number_grace_seconds() {\n\u001b[38;5;130m 29 \u001b[m local mysql_wait_timeout_ms=$(mysql -h127.0.0.1 -P6032 -uadmin -N --silent -e "select variable_value from global_variables where variable_name = \'mysql-wait_timm\u001b[31;1H\u001b[38;5;130m \u001b[meout\'")\n\u001b[38;5;130m 30 \u001b[m GRACE_PERIOD=$((($mysql_wait_timeout_ms+1000-1)/1000))\n\u001b[38;5;130m 31 \u001b[m}\n\u001b[38;5;130m 32 \n', + }, + tty: { + char_device: { + major: 4, + minor: 1, + }, + rows: 59, + columns: 173, }, }, }, @@ -152,6 +197,7 @@ export const sessionViewIOEventsMock: ProcessEventResults = { message: 'hello world security', event: { action: 'text_output', + id: '1', }, process: { entity_id: '2', @@ -167,6 +213,14 @@ export const sessionViewIOEventsMock: ProcessEventResults = { bytes_skipped: [], text: ' 33 \u001b[mfunction wait_for_connections() {\n\u001b[38;5;130m 34 \u001b[m local number_of_loops=$(((($GRACE_PERIOD+$SLEEP_INTERVAL-1)/$SLEEP_INTERVAL)))\n\u001b[38;5;130m 35 \n 36 \u001b[m echo "Waiting for connections to close for up to $GRACE_PERIOD seconds"\n\u001b[38;5;130m 37 \n 38 \u001b[m for i in $(seq 0 $number_of_loops); do\n\u001b[38;5;130m 39 \u001b[m\u001b[8Cget_number_db_connections\n\u001b[38;5;130m 40 \u001b[m\u001b[8Cif [[ $DB_CONNECTIONS_NUMBER -eq 0 ]]; then\n\u001b[38;5;130m 41 \u001b[m\u001b[12Cecho "No connection found for user $DB_USER to this node"\n\u001b[38;5;130m 42 \u001b[m\u001b[12Cbreak\n\u001b[38;5;130m 43 \u001b[m\u001b[8Celse\n\u001b[38;5;130m 44 \u001b[m\u001b[12Cecho "$DB_CONNECTIONS_NUMBER connection(s) found, waiting for ${SLEEP_INTERVAL}s, round $i"\n\u001b[38;5;130m 45 \u001b[m\u001b[12Csleep $SLEEP_INTERVAL\n\u001b[38;5;130m 46 \u001b[m\u001b[8Cfi\n\u001b[38;5;130m 47 \u001b[m done\n\u001b[38;5;130m 48 \u001b[m}\n\u001b[38;5;130m 49 \n 50 \u001b[mfunction parse_args() {\n\u001b[38;5;130m 51 \u001b[m while getopts \'hs:w:x\' opt; do\n\u001b[38;5;130m 52 \u001b[m\u001b[8Ccase "$opt" in\n\u001b[38;5;130m 53 \u001b[m\u001b[8Ch)\n\u001b[38;5;130m 54 \u001b[m\u001b[12Cusage\n\u001b[38;5;130m 55 \u001b[m\u001b[12C;;\n\u001b[38;5;130m 56 \u001b[m\u001b[8Cs)\u001b[1;9H\u001b[?25h\u001b[?25l\u001b[27m\u001b[29m\u001b[m\u001b[H\u001b[2J\u001b[1;1H\u001b[38;5;130m 58 \u001b[m\u001b[16C>&2 echo "Sleep interval (-s) must be a number"\n\u001b[38;5;130m 59 \u001b[m\u001b[16Cexit 1\n\u001b[38;5;130m 60 \u001b[m\u001b[12Cfi\n\u001b[38;5;130m 61 \u001b[m\u001b[12CARG_SLEEP_INTERVAL="$OPTARG"\n\u001b[38;5;130m 62 \u001b[m\u001b[12C;;\n\u001b[38;5;130m 63 \u001b[m\u001b[8Cw)\n\u001b[38;5;130m 64 \u001b[m\u001b[12Cif ! [[ $OPTARG =~ ^[0-9]+$ ]]; then\n\u001b[38;5;130m 65 \u001b[m\u001b[16C>&2 echo "Wait timeout (-w) must be a number"\n\u001b[38;5;130m 66 \u001b[m\u001b[16Cexit 1\n\u001b[38;5;130m 67 \u001b[m\u001b[12Cfi\n\u001b[38;5;130m 68 \u001b[m\u001b[12CARG_GRACE_PERIOD="$OPTARG"\n\u001b[38;5;130m 69 \u001b[m\u001b[12C;;\n\u001b[38;5;130m 70 \u001b[m\u001b[8Cx)\n\u001b[38;5;130m 71 \u001b[m\u001b[12CARG_KILL_IMMEDIATELY=1\n\u001b[38;5;130m 72 \u001b[m\u001b[12C;;\n\u001b[38;5;130m 73 \u001b[m\u001b[8Cesac\n\u001b[38;5;130m 74 \u001b[m done\n\u001b[38;5;130m 75 \n 76 \u001b[m GRACE_PERIOD=${ARG_GRACE_PERIOD:--1}\n\u001b[38;5;130m 77 \u001b[m SLEEP_INTERVAL=${ARG_SLEEP_INTERVAL:-30}\n\u001b[38;5;130m 78 \u001b[m KILL_IMMEDIATELY=${ARG_KILL_IMMEDIATELY:-0}\n\u001b[38;5;130m 79 \u001b[m}\n\u001b[38;5;130m 80 \n 81 \u001b[mDB_USER="rolap01"\n\u001b[38;5;130m 82 \n 83 \u001b[mparse_args $@\n', }, + tty: { + char_device: { + major: 4, + minor: 1, + }, + rows: 59, + columns: 173, + }, }, }, }, @@ -179,6 +233,7 @@ export const sessionViewIOEventsMock: ProcessEventResults = { message: 'hello world security', event: { action: 'text_output', + id: '1', }, process: { entity_id: '2', @@ -192,7 +247,15 @@ export const sessionViewIOEventsMock: ProcessEventResults = { total_bytes_captured: 1024, total_bytes_skipped: 0, bytes_skipped: [], - text: '\u001b[38;5;130m 84 \n 85 \u001b[mif [[ $KILL_IMMEDIATELY == 1 ]]; then\n\u001b[38;5;130m 86 \u001b[m echo "WARNING: Not waiting for connections to close gracefully"\n\u001b[38;5;130m 87 \u001b[m echo "Press any key to continue... wsrep_reject_queries will be set to \'ALL_KILL\'"\n\u001b[38;5;130m 88 \u001b[m read a\n\u001b[38;5;130m 89 \u001b[m mysql -h127.0.0.1 -P3306 -uroot -e "set global wsrep_reject_queries=\'ALL_KILL\'"\n\u001b[38;5;130m 90 \u001b[melse\n\u001b[38;5;130m 91 \u001b[m # Stop accepting queries in mariadb, do not kill opened connections\n\u001b[38;5;130m 92 \u001b[m mysql -h127.0.0.1 -P3306 -uroot -e "set global wsrep_reject_queries=\'ALL\'"\n\u001b[38;5;130m 93 \u001b[mfi\n\u001b[38;5;130m 94 \n 95 \u001b[mexit_code=$?\n\u001b[38;5;130m 96 \u001b[mif [[ $exit_code != 0 ]]; then\n\u001b[38;5;130m 97 \u001b[m >&2 echo "Failed to set the reject of queries on Galera node, exiting."\n\u001b[38;5;130m 98 \u001b[m exit $exit_code\n\u001b[38;5;130m 99 \u001b[melse\n\u001b[38;5;130m 100 \u001b[m echo "Successfully stopped accepting queries."\n\u001b[38;5;130m 101 \u001b[m if [[ $KILL_IMMEDIATELY == 1 ]]; then\n\u001b[38;5;130m 102 \u001b[m\u001b[8Cexit\n\u001b[38;5;130m 103 \u001b[m fi\n\u001b[38;5;130m 104 \u001b[mfi\n\u001b[38;5;130m 105 \n 106 \u001b[mif [[ $GRACE_PERIOD == -1 ]]; then\n\u001b[38;5;130m 107 \u001b[m set_number_grace_seconds\n\u001b[38;5;130m 108 \u001b[mfi\n\u001b[38;5;130m 109 \n 110 \u001b[mwait_for_connections\n\u001b[38;5;130m 111 \u001b[mif [[ $DB_CONNECTIONS_NUMBER != 0 ]]; then\n\u001b[38;5;130m 112 \u001b[m get_number_db_connections\n\u001b[38;5;130m 113 \u001b[m >&2 echo "ERROR: There are still $DB_CONNECTIONS_NUMBER opened DB connections."\n\u001b[38;5;130m 114 \u001b[m exit 3\n\u001b[38;5;130m 115 \u001b[mfi\b\b\u001b[?25h\u001b[?25l\nType :qa! and press to abandon all changes and exit Vim\u0007\u001b[58;9H\u001b[?25h\u0007\u001b[?25l\u001b[59;1H\u001b[K\u001b[59;1H:\u001b[?2004h\u001b[?25hqa!\r\u001b[?25l\u001b[?2004l\u001b[59;1H\u001b[K\u001b[59;1H\u001b[?2004l\u001b[?1l\u001b>\u001b[?25h\u001b[?1049l\u001b[23;0;0t,\u001bkroot@staging-host:~\u001b\\\n', + text: '\u001b[38;5;130m 84 \n 85 \u001b[mif [[ $KILL_IMMEDIATELY == 1 ]]; then\n\u001b[38;5;130m 86 \u001b[m echo "WARNING: Not waiting for connections to close gracefully"\n\u001b[38;5;130m 87 \u001b[m echo "Press any key to continue... wsrep_reject_queries will be set to \'ALL_KILL\'"\n\u001b[38;5;130m 88 \u001b[m read a\n\u001b[38;5;130m 89 \u001b[m mysql -h127.0.0.1 -P3306 -uroot -e "set global wsrep_reject_queries=\'ALL_KILL\'"\n\u001b[38;5;130m 90 \u001b[melse\n\u001b[38;5;130m 91 \u001b[m # Stop accepting queries in mariadb, do not kill opened connections\n\u001b[38;5;130m 92 \u001b[m mysql -h127.0.0.1 -P3306 -uroot -e "set global wsrep_reject_queries=\'ALL\'"\n\u001b[38;5;130m 93 \u001b[mfi\n\u001b[38;5;130m 94 \n 95 \u001b[mexit_code=$?\n\u001b[38;5;130m 96 \u001b[mif [[ $exit_code != 0 ]]; then\n\u001b[38;5;130m 97 \u001b[m >&2 echo "Failed to set the reject of queries on Mysql node, exiting."\n\u001b[38;5;130m 98 \u001b[m exit $exit_code\n\u001b[38;5;130m 99 \u001b[melse\n\u001b[38;5;130m 100 \u001b[m echo "Successfully stopped accepting queries."\n\u001b[38;5;130m 101 \u001b[m if [[ $KILL_IMMEDIATELY == 1 ]]; then\n\u001b[38;5;130m 102 \u001b[m\u001b[8Cexit\n\u001b[38;5;130m 103 \u001b[m fi\n\u001b[38;5;130m 104 \u001b[mfi\n\u001b[38;5;130m 105 \n 106 \u001b[mif [[ $GRACE_PERIOD == -1 ]]; then\n\u001b[38;5;130m 107 \u001b[m set_number_grace_seconds\n\u001b[38;5;130m 108 \u001b[mfi\n\u001b[38;5;130m 109 \n 110 \u001b[mwait_for_connections\n\u001b[38;5;130m 111 \u001b[mif [[ $DB_CONNECTIONS_NUMBER != 0 ]]; then\n\u001b[38;5;130m 112 \u001b[m get_number_db_connections\n\u001b[38;5;130m 113 \u001b[m >&2 echo "ERROR: There are still $DB_CONNECTIONS_NUMBER opened DB connections."\n\u001b[38;5;130m 114 \u001b[m exit 3\n\u001b[38;5;130m 115 \u001b[mfi\b\b\u001b[?25h\u001b[?25l\nType :qa! and press to abandon all changes and exit Vim\u0007\u001b[58;9H\u001b[?25h\u0007\u001b[?25l\u001b[59;1H\u001b[K\u001b[59;1H:\u001b[?2004h\u001b[?25hqa!\r\u001b[?25l\u001b[?2004l\u001b[59;1H\u001b[K\u001b[59;1H\u001b[?2004l\u001b[?1l\u001b>\u001b[?25h\u001b[?1049l\u001b[23;0;0t,\u001bkroot@staging-host:~\u001b\\\n', + }, + tty: { + char_device: { + major: 4, + minor: 1, + }, + rows: 59, + columns: 173, }, }, }, @@ -206,6 +269,7 @@ export const sessionViewIOEventsMock: ProcessEventResults = { message: 'hello world security', event: { action: 'text_output', + id: '1', }, process: { entity_id: '1', @@ -219,7 +283,15 @@ export const sessionViewIOEventsMock: ProcessEventResults = { total_bytes_captured: 1024, total_bytes_skipped: 0, bytes_skipped: [], - text: '\u001bkroot@staging-host:~\u001b\\\b\b\b\b\u001b[1P\b\b\b\b\u001b[1P\b\b\b\b\u001b[1P\b\b\b\b\b\b\b\b\b\n\u001bkroot@staging-host:~\u001b\\\b\u001b[K\b\u001b[K\b\u001b[K\n,\n22/05/26 09:24:09 rack-na/cl_md (md), Cluster ********\n[root@staging-host:~] vi -R /usr/local/bin/galera_traffic_start.sh\u0007\n22/05/26 09:25:32 rack-na/cl_md (md), Cluster ********\n[root@staging-host:~] vi -R /usr/local/bin/galera_traffic_start.sh.sh.sh.sho.shp.sh\n22/05/26 09:30:08 rack-na/cl_md (md), Cluster ********\n[root@staging-host:~] exi\u0007\u0007\u0007exitlogout\n,\u001bec2-user@staging-host:~\u001b\\\n\u001bec2-user@staging-host:~\u001b\\\n,\n22/05/26 09:24:01 rack-na/cl_md (md), Cluster ********\n[ec2-user@staging-host:~] sudo -i\n22/05/26 10:11:37 rack-na/cl_md (md), Cluster ********\n[ec2-user@staging-host:~] exitlogout\n\n', + text: '\u001bkroot@staging-host:~\u001b\\\b\b\b\b\u001b[1P\b\b\b\b\u001b[1P\b\b\b\b\u001b[1P\b\b\b\b\b\b\b\b\b\n\u001bkroot@staging-host:~\u001b\\\b\u001b[K\b\u001b[K\b\u001b[K\n,\n22/05/26 09:24:09 rack-na/cl_md (md), Cluster ********\n[root@staging-host:~] vi -R /usr/local/bin/script_one.sh\u0007\n22/05/26 09:25:32 rack-na/cl_md (md), Cluster ********\n[root@staging-host:~] vi -R /usr/local/bin/script_one.sh.sh.sh.sho.shp.sh\n22/05/26 09:30:08 rack-na/cl_md (md), Cluster ********\n[root@staging-host:~] exi\u0007\u0007\u0007exitlogout\n,\u001bec2-user@staging-host:~\u001b\\\n\u001bec2-user@staging-host:~\u001b\\\n,\n22/05/26 09:24:01 rack-na/cl_md (md), Cluster ********\n[ec2-user@staging-host:~] sudo -i\n22/05/26 10:11:37 rack-na/cl_md (md), Cluster ********\n[ec2-user@staging-host:~] exitlogout\n\n', + }, + tty: { + char_device: { + major: 4, + minor: 1, + }, + rows: 59, + columns: 173, }, }, }, diff --git a/x-pack/plugins/session_view/common/types/process_tree/index.ts b/x-pack/plugins/session_view/common/types/process_tree/index.ts index e620b654db16f..a2b40347f1b5f 100644 --- a/x-pack/plugins/session_view/common/types/process_tree/index.ts +++ b/x-pack/plugins/session_view/common/types/process_tree/index.ts @@ -60,16 +60,13 @@ export interface Teletype { major?: number; minor?: number; }; + rows?: number; + columns?: number; } -// used by tty_player component to split process.io.text into lines of IO export interface IOLine { - value?: string; - - // the following is only set client side for caching purposes - process_name?: string; - process_entity_id?: string; - process_entity_cursor?: string; + event: ProcessEvent; + value: string; } export interface IOFields { diff --git a/x-pack/plugins/session_view/public/components/session_view/hooks.ts b/x-pack/plugins/session_view/public/components/session_view/hooks.ts index 2d24305cc02d7..06a1e0125aea1 100644 --- a/x-pack/plugins/session_view/public/components/session_view/hooks.ts +++ b/x-pack/plugins/session_view/public/components/session_view/hooks.ts @@ -5,7 +5,7 @@ * 2.0. */ import { useEffect, useState, useMemo } from 'react'; -import { useQuery, useInfiniteQuery } from 'react-query'; +import { useQuery, useInfiniteQuery } from '@tanstack/react-query'; import { EuiSearchBarOnChangeArgs } from '@elastic/eui'; import { CoreStart } from '@kbn/core/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; diff --git a/x-pack/plugins/session_view/public/components/session_view/index.test.tsx b/x-pack/plugins/session_view/public/components/session_view/index.test.tsx index 26b5d9c3cc7f9..215fa55747098 100644 --- a/x-pack/plugins/session_view/public/components/session_view/index.test.tsx +++ b/x-pack/plugins/session_view/public/components/session_view/index.test.tsx @@ -23,8 +23,6 @@ describe('SessionView component', () => { let mockedContext: AppContextTestRender; let mockedApi: AppContextTestRender['coreStart']['http']['get']; - const waitForApiCall = () => waitFor(() => expect(mockedApi).toHaveBeenCalled()); - beforeEach(() => { mockedContext = createAppRootMockRenderer(); mockedApi = mockedContext.coreStart.http.get; @@ -47,10 +45,11 @@ describe('SessionView component', () => { // make the request wait mockedApi.mockReturnValue(new Promise((resolve) => (releaseApiResponse = resolve))); render(); - await waitForApiCall(); // see if loader is present - expect(renderResult.getByTestId('sectionLoading')).toBeTruthy(); + await waitFor(() => { + expect(renderResult.getByTestId('sectionLoading')).toBeTruthy(); + }); // release the request releaseApiResponse!(mockedApi); @@ -61,13 +60,15 @@ describe('SessionView component', () => { it('should show the Empty message', async () => { render(); - await waitForApiCall(); - expect(renderResult.getByTestId('sessionView:sessionViewProcessEventsEmpty')).toBeTruthy(); + await waitFor(() => { + expect( + renderResult.getByTestId('sessionView:sessionViewProcessEventsEmpty') + ).toBeTruthy(); + }); }); it('should not display the search bar', async () => { render(); - await waitForApiCall(); expect( renderResult.queryByTestId('sessionView:sessionViewProcessEventsSearch') ).toBeFalsy(); @@ -85,10 +86,11 @@ describe('SessionView component', () => { // make the request wait mockedApi.mockReturnValue(new Promise((resolve) => (releaseApiResponse = resolve))); render(); - await waitForApiCall(); // see if loader is present - expect(renderResult.getByTestId('sectionLoading')).toBeTruthy(); + await waitFor(() => { + expect(renderResult.getByTestId('sectionLoading')).toBeTruthy(); + }); // release the request releaseApiResponse!(mockedApi); @@ -99,20 +101,28 @@ describe('SessionView component', () => { it('should display the search bar', async () => { render(); - await waitForApiCall(); - expect(renderResult.getByTestId('sessionView:sessionViewProcessEventsSearch')).toBeTruthy(); + + await waitFor(() => { + expect( + renderResult.getByTestId('sessionView:sessionViewProcessEventsSearch') + ).toBeTruthy(); + }); }); it('should show items on the list, and auto selects session leader', async () => { render(); - await waitForApiCall(); - expect(renderResult.getAllByTestId('sessionView:processTreeNode')).toBeTruthy(); + await waitFor(() => { + expect(renderResult.getAllByTestId('sessionView:processTreeNode')).toBeTruthy(); + }); }); it('should toggle detail panel visibilty when detail button clicked', async () => { render(); - await waitForApiCall(); + + await waitFor(() => { + expect(renderResult.getByTestId('sessionView:sessionViewDetailPanelToggle')).toBeTruthy(); + }); userEvent.click(renderResult.getByTestId('sessionView:sessionViewDetailPanelToggle')); expect(renderResult.getByText('Process')).toBeTruthy(); @@ -122,7 +132,11 @@ describe('SessionView component', () => { it('should render session view options button and its options when clicked', async () => { render(); - await waitForApiCall(); + + await waitFor(() => { + expect(renderResult.getByTestId('sessionView:sessionViewOptionButton')).toBeTruthy(); + }); + userEvent.click(renderResult.getByTestId('sessionView:sessionViewOptionButton')); expect(renderResult.getByText('Display options')).toBeTruthy(); expect(renderResult.getByText('Timestamp')).toBeTruthy(); @@ -131,9 +145,10 @@ describe('SessionView component', () => { it('should show refresh button', async () => { render(); - await waitForApiCall(); - expect(renderResult.getAllByTestId('sessionView:sessionViewRefreshButton')).toBeTruthy(); + await waitFor(() => { + expect(renderResult.getAllByTestId('sessionView:sessionViewRefreshButton')).toBeTruthy(); + }); }); }); @@ -150,9 +165,10 @@ describe('SessionView component', () => { }); render(); - await waitForApiCall(); - expect(renderResult.queryByTestId('sessionView:TTYPlayerToggle')).toBeTruthy(); + await waitFor(() => { + expect(renderResult.queryByTestId('sessionView:TTYPlayerToggle')).toBeTruthy(); + }); }); it('should NOT show tty player button, if session has no output', async () => { @@ -171,9 +187,10 @@ describe('SessionView component', () => { }); render(); - await waitForApiCall(); - expect(renderResult.queryByTestId('sessionView:TTYPlayerToggle')).toBeFalsy(); + await waitFor(() => { + expect(renderResult.queryByTestId('sessionView:TTYPlayerToggle')).toBeFalsy(); + }); }); }); }); diff --git a/x-pack/plugins/session_view/public/components/session_view/index.tsx b/x-pack/plugins/session_view/public/components/session_view/index.tsx index e2d795283c7ce..921a3f7ce8b9c 100644 --- a/x-pack/plugins/session_view/public/components/session_view/index.tsx +++ b/x-pack/plugins/session_view/public/components/session_view/index.tsx @@ -133,8 +133,8 @@ export const SessionView = ({ } = useFetchSessionViewAlerts(sessionEntityId, investigatedAlertId); const handleRefresh = useCallback(() => { - refetch({ refetchPage: (page, index, allPages) => allPages.length - 1 === index }); - refetchAlerts({ refetchPage: (page, index, allPages) => allPages.length - 1 === index }); + refetch({ refetchPage: (_page, index, allPages) => allPages.length - 1 === index }); + refetchAlerts({ refetchPage: (_page, index, allPages) => allPages.length - 1 === index }); }, [refetch, refetchAlerts]); const alerts = useMemo(() => { diff --git a/x-pack/plugins/session_view/public/components/tty_player/hooks.test.tsx b/x-pack/plugins/session_view/public/components/tty_player/hooks.test.tsx index 23f81d7941439..fb90951874385 100644 --- a/x-pack/plugins/session_view/public/components/tty_player/hooks.test.tsx +++ b/x-pack/plugins/session_view/public/components/tty_player/hooks.test.tsx @@ -8,7 +8,9 @@ import { renderHook, act } from '@testing-library/react-hooks'; import { sessionViewIOEventsMock } from '../../../common/mocks/responses/session_view_io_events.mock'; import { useIOLines, useXtermPlayer, XtermPlayerDeps } from './hooks'; import { ProcessEventsPage } from '../../../common/types/process_tree'; -import { DEFAULT_TTY_PLAYSPEED_MS } from '../../../common/constants'; +import { DEFAULT_TTY_FONT_SIZE, DEFAULT_TTY_PLAYSPEED_MS } from '../../../common/constants'; + +const VIM_LINE_START = 19; describe('TTYPlayer/hooks', () => { beforeAll(() => { @@ -66,10 +68,11 @@ describe('TTYPlayer/hooks', () => { initialProps = { ref: mockRef, isPlaying: false, + setIsPlaying: jest.fn(), lines, hasNextPage: false, fetchNextPage: () => null, - isFullscreen: false, + fontSize: DEFAULT_TTY_FONT_SIZE, }; }); @@ -88,7 +91,7 @@ describe('TTYPlayer/hooks', () => { expect(currentLine).toBe(0); act(() => { - seekToLine(17); // line where vim output starts + seekToLine(VIM_LINE_START); // line where vim output starts }); jest.advanceTimersByTime(100); @@ -102,14 +105,14 @@ describe('TTYPlayer/hooks', () => { }); act(() => { - xTermResult.current.seekToLine(17); // line where vim output starts + xTermResult.current.seekToLine(VIM_LINE_START); // line where vim output starts }); jest.advanceTimersByTime(100); const { terminal, currentLine } = xTermResult.current; - expect(currentLine).toBe(17); + expect(currentLine).toBe(VIM_LINE_START); expect(terminal.buffer.active.getLine(0)?.translateToString(true)).toBe('#!/bin/env bash'); }); @@ -152,7 +155,7 @@ describe('TTYPlayer/hooks', () => { act(() => { jest.advanceTimersByTime(DEFAULT_TTY_PLAYSPEED_MS * initialProps.lines.length + 100); }); - expect(result.current.currentLine).toBe(initialProps.lines.length); + expect(result.current.currentLine).toBe(initialProps.lines.length - 1); }); it('will allow a plain text search highlight on the last line printed', async () => { diff --git a/x-pack/plugins/session_view/public/components/tty_player/hooks.ts b/x-pack/plugins/session_view/public/components/tty_player/hooks.ts index a37f8899d0ed4..0138775199c6d 100644 --- a/x-pack/plugins/session_view/public/components/tty_player/hooks.ts +++ b/x-pack/plugins/session_view/public/components/tty_player/hooks.ts @@ -6,13 +6,16 @@ */ import { Terminal } from 'xterm'; import 'xterm/css/xterm.css'; -import { FitAddon } from 'xterm-addon-fit'; import { useMemo, useState, useEffect, useCallback } from 'react'; -import { useInfiniteQuery } from 'react-query'; +import { useInfiniteQuery } from '@tanstack/react-query'; import { CoreStart } from '@kbn/core/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { SearchAddon } from './xterm_search'; import { useEuiTheme } from '../../hooks'; + +// eslint-disable-next-line @kbn/imports/no_boundary_crossing +import { sessionViewIOEventsMock } from '../../../common/mocks/responses/session_view_io_events.mock'; + import { IOLine, ProcessEvent, @@ -24,8 +27,12 @@ import { IO_EVENTS_PER_PAGE, QUERY_KEY_IO_EVENTS, DEFAULT_TTY_PLAYSPEED_MS, + DEFAULT_TTY_FONT_SIZE, + TTY_LINE_SPLITTER_REGEX, } from '../../../common/constants'; +const MOCK_DEBUG = false; // This code will be removed once we have an agent to test with. + export const useFetchIOEvents = (sessionEntityId: string) => { const { http } = useKibana().services; const cachingKeys = useMemo(() => [QUERY_KEY_IO_EVENTS, sessionEntityId], [sessionEntityId]); @@ -41,13 +48,18 @@ export const useFetchIOEvents = (sessionEntityId: string) => { }, }); + if (MOCK_DEBUG) { + res.events = sessionViewIOEventsMock.events; + res.total = res.events?.length || 0; + } + const events = res.events?.map((event: any) => event._source as ProcessEvent) ?? []; return { events, cursor, total: res.total }; }, { getNextPageParam: (lastPage) => { - if (lastPage.events.length >= IO_EVENTS_PER_PAGE) { + if (!MOCK_DEBUG && lastPage.events.length >= IO_EVENTS_PER_PAGE) { return { cursor: lastPage.events[lastPage.events.length - 1]['@timestamp'], }; @@ -77,9 +89,21 @@ export const useIOLines = (pages: ProcessEventsPage[] | undefined) => { return pages.reduce((previous, current) => { if (current.events) { current.events.forEach((event) => { - if (event?.process?.io?.text) { - const data: IOLine[] = event.process.io.text.split(/\n\r?/).map((line) => { + const { process } = event; + if (process?.io?.text !== undefined) { + const splitLines = process.io.text.split(TTY_LINE_SPLITTER_REGEX); + const combinedLines = [splitLines[0]]; + + // delimiters e.g \r\n or cursor movements are merged with their line text + // we start on an odd number so that cursor movements happen at the start of each line + // this is needed for the search to work accurately + for (let i = 1; i < splitLines.length - 1; i = i + 2) { + combinedLines.push(splitLines[i] + splitLines[i + 1]); + } + + const data: IOLine[] = combinedLines.map((line) => { return { + event, // pointer to the event so it's easy to look up other details for the line value: line, }; }); @@ -99,85 +123,94 @@ export const useIOLines = (pages: ProcessEventsPage[] | undefined) => { export interface XtermPlayerDeps { ref: React.RefObject; isPlaying: boolean; + setIsPlaying(value: boolean): void; lines: IOLine[]; + fontSize: number; hasNextPage?: boolean; fetchNextPage?: () => void; - isFullscreen?: boolean; } export const useXtermPlayer = ({ ref, isPlaying, + setIsPlaying, lines, + fontSize, hasNextPage, fetchNextPage, - isFullscreen, }: XtermPlayerDeps) => { const { euiTheme } = useEuiTheme(); const { font, colors } = euiTheme; const [currentLine, setCurrentLine] = useState(0); - const [userSeeked, setUserSeeked] = useState(false); const [playSpeed] = useState(DEFAULT_TTY_PLAYSPEED_MS); // potentially configurable + const tty = lines?.[currentLine]?.event.process?.tty; - const [terminal, fitAddon, searchAddon] = useMemo(() => { + const [terminal, searchAddon] = useMemo(() => { const term = new Terminal({ theme: { - background: 'rgba(0,0,0,0)', selection: colors.warning, }, fontFamily: font.familyCode, - fontSize: 11, - allowTransparency: true, + fontSize: DEFAULT_TTY_FONT_SIZE, + scrollback: 0, + convertEol: true, }); - const fitInstance = new FitAddon(); const searchInstance = new SearchAddon(); - - term.loadAddon(fitInstance); term.loadAddon(searchInstance); - return [term, fitInstance, searchInstance]; - }, [colors, font]); + return [term, searchInstance]; + }, [font, colors]); useEffect(() => { - if (ref.current) { + if (ref.current && !terminal.element) { terminal.open(ref.current); } }, [terminal, ref]); - useEffect(() => { - // isFullscreen check is there just to avoid the necessary "unnecessary" react-hook dep - // When isFullscreen changes, e.g goes from false to true and vice versa, we need to call fit. - if (isFullscreen !== undefined) { - fitAddon.fit(); - } - }, [isFullscreen, fitAddon]); - const render = useCallback( - (lineNumber: number) => { + (lineNumber: number, clear: boolean) => { if (lines.length === 0) { return; } let linesToPrint; - if (userSeeked) { - linesToPrint = lines.slice(0, lineNumber); + if (clear) { + linesToPrint = lines.slice(0, lineNumber + 1); + terminal.reset(); terminal.clear(); - setUserSeeked(false); } else { linesToPrint = [lines[lineNumber]]; } linesToPrint.forEach((line, index) => { if (line?.value !== undefined) { - terminal.writeln(line.value); + terminal.write(line.value); } }); }, - [terminal, lines, userSeeked] + [terminal, lines] ); + useEffect(() => { + const fontChanged = terminal.getOption('fontSize') !== fontSize; + const ttyChanged = tty && (terminal.rows !== tty?.rows || terminal.cols !== tty?.columns); + + if (fontChanged) { + terminal.setOption('fontSize', fontSize); + } + + if (tty?.rows && tty?.columns && ttyChanged) { + terminal.resize(tty.columns, tty.rows); + } + + if (fontChanged || ttyChanged) { + // clear and rerender + render(currentLine, true); + } + }, [currentLine, fontSize, terminal, render, tty]); + useEffect(() => { if (isPlaying) { const timer = setTimeout(() => { @@ -185,29 +218,32 @@ export const useXtermPlayer = ({ return; } - if (currentLine < lines.length) { + if (currentLine < lines.length - 1) { setCurrentLine(currentLine + 1); } + + render(currentLine, false); + + if (hasNextPage && fetchNextPage && currentLine === lines.length - 1) { + fetchNextPage(); + } }, playSpeed); return () => { - clearInterval(timer); + clearTimeout(timer); }; } - }, [lines, currentLine, isPlaying, playSpeed]); + }, [lines, currentLine, isPlaying, playSpeed, render, hasNextPage, fetchNextPage]); - useEffect(() => { - render(currentLine); - - if (hasNextPage && fetchNextPage && currentLine === lines.length - 1) { - fetchNextPage(); - } - }, [fetchNextPage, currentLine, lines, render, hasNextPage]); + const seekToLine = useCallback( + (index) => { + setCurrentLine(index); + setIsPlaying(false); - const seekToLine = useCallback((line) => { - setUserSeeked(true); - setCurrentLine(line); - }, []); + render(index, true); + }, + [setIsPlaying, render] + ); const search = useCallback( (query: string, startCol: number) => { @@ -216,15 +252,10 @@ export const useXtermPlayer = ({ [searchAddon] ); - const fit = useCallback(() => { - fitAddon.fit(); - }, [fitAddon]); - return { terminal, currentLine, seekToLine, search, - fit, }; }; diff --git a/x-pack/plugins/session_view/public/components/tty_player/index.tsx b/x-pack/plugins/session_view/public/components/tty_player/index.tsx index 48dbf82441e42..7d3656d8ba7b7 100644 --- a/x-pack/plugins/session_view/public/components/tty_player/index.tsx +++ b/x-pack/plugins/session_view/public/components/tty_player/index.tsx @@ -7,6 +7,7 @@ import React, { useRef, useState, useCallback, ChangeEvent, MouseEvent } from 'react'; import { EuiPanel, EuiRange, EuiFlexGroup, EuiFlexItem, EuiButtonIcon } from '@elastic/eui'; import { TTYSearchBar } from '../tty_search_bar'; +import { TTYTextSizer } from '../tty_text_sizer'; import { useStyles } from './styles'; import { useFetchIOEvents, useIOLines, useXtermPlayer } from './hooks'; @@ -16,22 +17,31 @@ export interface TTYPlayerDeps { isFullscreen: boolean; } +const DEFAULT_FONT_SIZE = 11; + export const TTYPlayer = ({ sessionEntityId, onClose, isFullscreen }: TTYPlayerDeps) => { - const styles = useStyles(); - const ref = useRef(null); + const ref = useRef(null); + const scrollRef = useRef(null); const { data, fetchNextPage, hasNextPage } = useFetchIOEvents(sessionEntityId); const lines = useIOLines(data?.pages); + + const [fontSize, setFontSize] = useState(DEFAULT_FONT_SIZE); const [isPlaying, setIsPlaying] = useState(false); + const { search, currentLine, seekToLine } = useXtermPlayer({ ref, isPlaying, + setIsPlaying, lines, + fontSize, hasNextPage, fetchNextPage, - isFullscreen, }); + const tty = lines?.[currentLine]?.event?.process?.tty; + const styles = useStyles(tty); + const onLineChange = useCallback( (event: ChangeEvent | MouseEvent) => { const line = parseInt((event?.target as HTMLInputElement).value || '0', 10); @@ -65,7 +75,10 @@ export const TTYPlayer = ({ sessionEntityId, onClose, isFullscreen }: TTYPlayerD -
+ +
+
+
{/* the following will be replaced by a new component */} + + +
diff --git a/x-pack/plugins/session_view/public/components/tty_player/styles.ts b/x-pack/plugins/session_view/public/components/tty_player/styles.ts index c4061c8a64dce..e1d18e3075027 100644 --- a/x-pack/plugins/session_view/public/components/tty_player/styles.ts +++ b/x-pack/plugins/session_view/public/components/tty_player/styles.ts @@ -7,15 +7,14 @@ import { useMemo } from 'react'; import { CSSObject, css } from '@emotion/react'; -import { transparentize, useEuiScrollBar } from '@elastic/eui'; +import { transparentize } from '@elastic/eui'; import { useEuiTheme } from '../../hooks'; +import { Teletype } from '../../../common/types/process_tree'; -export const useStyles = () => { +export const useStyles = (tty?: Teletype) => { const { euiTheme } = useEuiTheme(); - const euiScrollBar = useEuiScrollBar(); - const cached = useMemo(() => { - const { size, colors, border } = euiTheme; + const { size, font, colors, border } = euiTheme; const container: CSSObject = { position: 'absolute', @@ -23,6 +22,7 @@ export const useStyles = () => { width: '100%', height: '100%', overflow: 'hidden', + zIndex: 10, borderRadius: size.s, backgroundColor: colors.ink, '.euiRangeLevel--warning': { @@ -36,20 +36,47 @@ export const useStyles = () => { }, }; + const windowBoundsColor = transparentize(colors.ghost, 0.6); + const terminal: CSSObject = { + minHeight: '100%', + '.xterm': css` + display: inline-block; + `, + '.xterm-screen': css` + overflow-y: visible; + border: ${border.width.thin} dotted ${windowBoundsColor}; + border-top: 0; + border-left: 0; + box-sizing: content-box; + `, + }; + + if (tty?.rows) { + terminal['.xterm-screen:after'] = css` + position: absolute; + right: ${size.s}; + top: ${size.s}; + content: '${tty?.columns}x${tty?.rows}'; + color: ${windowBoundsColor}; + font-family: ${font.familyCode}; + font-size: ${size.m}; + `; + } + + const scrollPane: CSSObject = { width: '100%', height: 'calc(100% - 142px)', - '.xterm-viewport': css` - ${euiScrollBar} - `, border: border.thin, + overflow: 'auto', }; return { container, terminal, + scrollPane, }; - }, [euiScrollBar, euiTheme]); + }, [tty, euiTheme]); return cached; }; diff --git a/x-pack/plugins/session_view/public/components/tty_player/translations.ts b/x-pack/plugins/session_view/public/components/tty_player/translations.ts new file mode 100644 index 0000000000000..244a5a355b0ec --- /dev/null +++ b/x-pack/plugins/session_view/public/components/tty_player/translations.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { i18n } from '@kbn/i18n'; + +export const BETA = i18n.translate('xpack.sessionView.beta', { + defaultMessage: 'Beta', +}); + +export const REFRESH_SESSION = i18n.translate('xpack.sessionView.refreshSession', { + defaultMessage: 'Refresh session', +}); + +export const OPEN_TTY_PLAYER = i18n.translate('xpack.sessionView.openTTYPlayer', { + defaultMessage: 'Open TTY player', +}); diff --git a/x-pack/plugins/session_view/public/components/tty_player/xterm_search.ts b/x-pack/plugins/session_view/public/components/tty_player/xterm_search.ts index 258920319676c..3c430d691e3f7 100644 --- a/x-pack/plugins/session_view/public/components/tty_player/xterm_search.ts +++ b/x-pack/plugins/session_view/public/components/tty_player/xterm_search.ts @@ -94,7 +94,7 @@ export class SearchAddon implements ITerminalAddon { } if (searchOptions?.lastLineOnly) { - startRow = this._terminal.buffer.active.cursorY - 1; + startRow = this._terminal.buffer.active.cursorY; startCol = searchOptions?.startCol || 0; } @@ -176,10 +176,9 @@ export class SearchAddon implements ITerminalAddon { // Start from selection start if there is a selection startRow = currentSelection.startRow; startCol = currentSelection.startColumn; - } - - if (searchOptions?.lastLineOnly) { + } else if (searchOptions?.lastLineOnly) { startRow = this._terminal.buffer.active.cursorY - 1; + startCol = this._terminal.cols; } this._initLinesCache(); diff --git a/x-pack/plugins/session_view/public/components/tty_search_bar/index.test.tsx b/x-pack/plugins/session_view/public/components/tty_search_bar/index.test.tsx index 4545acf1a4578..77e9c4576c565 100644 --- a/x-pack/plugins/session_view/public/components/tty_search_bar/index.test.tsx +++ b/x-pack/plugins/session_view/public/components/tty_search_bar/index.test.tsx @@ -56,7 +56,9 @@ describe('TTYSearchBar component', () => { // there is a slight delay in the seek in xtermjs, so we wait 100ms before trying to highlight a result. await new Promise((r) => setTimeout(r, 100)); - expect(props.xTermSearchFn).toHaveBeenCalledTimes(1); + expect(props.xTermSearchFn).toHaveBeenCalledTimes(2); + expect(props.xTermSearchFn).toHaveBeenNthCalledWith(1, '', 0); + expect(props.xTermSearchFn).toHaveBeenNthCalledWith(2, '-h', 6); }); it('calls seekToline and xTermSearchFn when currentMatch changes', async () => { @@ -76,12 +78,13 @@ describe('TTYSearchBar component', () => { // two calls, first instance -h is at line 22, 2nd at line 42 expect(props.seekToLine).toHaveBeenCalledTimes(2); - expect(props.seekToLine).toHaveBeenNthCalledWith(1, 22); - expect(props.seekToLine).toHaveBeenNthCalledWith(2, 42); + expect(props.seekToLine).toHaveBeenNthCalledWith(1, 24); + expect(props.seekToLine).toHaveBeenNthCalledWith(2, 94); - expect(props.xTermSearchFn).toHaveBeenCalledTimes(2); - expect(props.xTermSearchFn).toHaveBeenNthCalledWith(1, '-h', 6); - expect(props.xTermSearchFn).toHaveBeenNthCalledWith(2, '-h', 13); + expect(props.xTermSearchFn).toHaveBeenCalledTimes(3); + expect(props.xTermSearchFn).toHaveBeenNthCalledWith(1, '', 0); + expect(props.xTermSearchFn).toHaveBeenNthCalledWith(2, '-h', 6); + expect(props.xTermSearchFn).toHaveBeenNthCalledWith(3, '-h', 13); }); it('calls xTermSearchFn with empty query when search is cleared', async () => { @@ -97,6 +100,6 @@ describe('TTYSearchBar component', () => { userEvent.click(renderResult.getByTestId('clearSearchButton')); await new Promise((r) => setTimeout(r, 100)); - expect(props.xTermSearchFn).toHaveBeenNthCalledWith(2, '', 0); + expect(props.xTermSearchFn).toHaveBeenNthCalledWith(3, '', 0); }); }); diff --git a/x-pack/plugins/session_view/public/components/tty_search_bar/index.tsx b/x-pack/plugins/session_view/public/components/tty_search_bar/index.tsx index af60bdf664f9d..e5081a217dec1 100644 --- a/x-pack/plugins/session_view/public/components/tty_search_bar/index.tsx +++ b/x-pack/plugins/session_view/public/components/tty_search_bar/index.tsx @@ -4,9 +4,10 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useEffect, useMemo, useState, useCallback } from 'react'; +import React, { useMemo, useState, useCallback } from 'react'; import { SessionViewSearchBar } from '../session_view_search_bar'; import { IOLine } from '../../../common/types/process_tree'; +import { TTY_STRIP_CONTROL_CODES_REGEX } from '../../../common/constants'; interface SearchResult { line: IOLine; @@ -24,50 +25,71 @@ export const TTYSearchBar = ({ lines, seekToLine, xTermSearchFn }: TTYSearchBarD const [currentMatch, setCurrentMatch] = useState(null); const [searchQuery, setSearchQuery] = useState(''); - useEffect(() => { - if (currentMatch) { - const goToLine = lines.indexOf(currentMatch.line); - seekToLine(goToLine); - } + const jumpToMatch = useCallback( + (match) => { + if (match) { + const goToLine = lines.indexOf(match.line); + seekToLine(goToLine); + } - const timeout = setTimeout(() => { - return xTermSearchFn(searchQuery, currentMatch?.index || 0); - }, 100); + const timeout = setTimeout(() => { + return xTermSearchFn(searchQuery, match?.index || 0); + }, 100); - return () => { - clearTimeout(timeout); - }; - }, [currentMatch, searchQuery, lines, xTermSearchFn, seekToLine]); + return () => { + clearTimeout(timeout); + }; + }, + [lines, seekToLine, xTermSearchFn, searchQuery] + ); const searchResults = useMemo(() => { - if (searchQuery) { - const matches: SearchResult[] = []; + const matches: SearchResult[] = []; + if (searchQuery) { lines.reduce((previous: SearchResult[], current: IOLine) => { if (current.value) { + // check for cursor movement at the start of the line + const cursorMovement = current.value.match(/^\x1b\[\d+;(\d+)(H|d)/); const regex = new RegExp(searchQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'ig'); - const lineMatches = current.value.matchAll(regex); + const lineMatches = current.value + .replace(TTY_STRIP_CONTROL_CODES_REGEX, '') + .replace(/^\r?\n/, '') + .matchAll(regex); + if (lineMatches) { for (const match of lineMatches) { - previous.push({ line: current, match: match[0], index: match.index || 0 }); + let matchOffset = 0; + + if (cursorMovement) { + // the column position 1 based e.g \x1b[39;5H means row 39 column 5 + matchOffset = parseInt(cursorMovement[1], 10) - 3; + } + + previous.push({ + line: current, + match: match[0], + index: matchOffset + (match.index || 0), + }); } } } return previous; }, matches); + } - if (matches.length > 0) { - setCurrentMatch(matches[0]); - } else { - setCurrentMatch(null); - } - - return matches; + if (matches.length > 0) { + const firstMatch = matches[0]; + setCurrentMatch(firstMatch); + jumpToMatch(firstMatch); + } else { + setCurrentMatch(null); + xTermSearchFn('', 0); } - return []; - }, [searchQuery, lines]); + return matches; + }, [searchQuery, lines, jumpToMatch, xTermSearchFn]); const onSearch = useCallback((query) => { setSearchQuery(query); @@ -80,9 +102,10 @@ export const TTYSearchBar = ({ lines, seekToLine, xTermSearchFn }: TTYSearchBarD if (match && currentMatch !== match) { setCurrentMatch(match); + jumpToMatch(match); } }, - [currentMatch, searchResults] + [jumpToMatch, currentMatch, searchResults] ); return ( diff --git a/x-pack/plugins/session_view/public/components/tty_text_sizer/index.test.tsx b/x-pack/plugins/session_view/public/components/tty_text_sizer/index.test.tsx new file mode 100644 index 0000000000000..ec9e8ffd3cab2 --- /dev/null +++ b/x-pack/plugins/session_view/public/components/tty_text_sizer/index.test.tsx @@ -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 React from 'react'; +import userEvent from '@testing-library/user-event'; +import { AppContextTestRender, createAppRootMockRenderer } from '../../test'; +import { DEFAULT_TTY_FONT_SIZE } from '../../../common/constants'; +import { TTYTextSizer, TTYTextSizerDeps } from '.'; + +describe('TTYTextSizer component', () => { + let render: () => ReturnType; + let renderResult: ReturnType; + let mockedContext: AppContextTestRender; + let props: TTYTextSizerDeps; + + beforeEach(() => { + mockedContext = createAppRootMockRenderer(); + + props = { + tty: { + rows: 24, + columns: 80, + }, + containerHeight: 200, + fontSize: DEFAULT_TTY_FONT_SIZE, + onFontSizeChanged: jest.fn(), + }; + }); + + it('mounts and renders the text sizer controls', async () => { + renderResult = mockedContext.render(); + expect(renderResult.queryByTestId('sessionView:TTYTextSizer')).toBeTruthy(); + }); + + it('emits a fontSize which will fit the container when ZoomFit clicked', async () => { + renderResult = mockedContext.render(); + + const zoomFitBtn = renderResult.queryByTestId('sessionView:TTYZoomFit'); + + if (zoomFitBtn) { + userEvent.click(zoomFitBtn); + } + + expect(props.onFontSizeChanged).toHaveBeenCalledTimes(1); + expect(props.onFontSizeChanged).toHaveBeenCalledWith(6.41025641025641); + }); + + it('emits a larger fontSize when zoom in clicked', async () => { + renderResult = mockedContext.render(); + + const zoomInBtn = renderResult.queryByTestId('sessionView:TTYZoomIn'); + + if (zoomInBtn) { + userEvent.click(zoomInBtn); + } + + expect(props.onFontSizeChanged).toHaveBeenCalledTimes(1); + expect(props.onFontSizeChanged).toHaveBeenCalledWith(DEFAULT_TTY_FONT_SIZE + 1); + }); + + it('emits a smaller fontSize when zoom out clicked', async () => { + renderResult = mockedContext.render(); + + const zoomOutBtn = renderResult.queryByTestId('sessionView:TTYZoomOut'); + + if (zoomOutBtn) { + userEvent.click(zoomOutBtn); + } + + expect(props.onFontSizeChanged).toHaveBeenCalledTimes(1); + expect(props.onFontSizeChanged).toHaveBeenCalledWith(DEFAULT_TTY_FONT_SIZE - 1); + }); +}); diff --git a/x-pack/plugins/session_view/public/components/tty_text_sizer/index.tsx b/x-pack/plugins/session_view/public/components/tty_text_sizer/index.tsx new file mode 100644 index 0000000000000..463dbbbf80f69 --- /dev/null +++ b/x-pack/plugins/session_view/public/components/tty_text_sizer/index.tsx @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { useCallback, useMemo } from 'react'; +import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { Teletype } from '../../../common/types/process_tree'; +import { DEFAULT_TTY_FONT_SIZE } from '../../../common/constants'; +import { ZOOM_IN, ZOOM_FIT, ZOOM_OUT } from './translations'; + +export interface TTYTextSizerDeps { + tty?: Teletype; + containerHeight: number; + fontSize: number; + onFontSizeChanged(newSize: number): void; +} + +const LINE_HEIGHT_SCALE_RATIO = 1.3; +const MINIMUM_FONT_SIZE = 2; +const MAXIMUM_FONT_SIZE = 20; + +export const TTYTextSizer = ({ + tty, + containerHeight, + fontSize, + onFontSizeChanged, +}: TTYTextSizerDeps) => { + const onFitFontSize = useMemo(() => { + if (tty?.rows && containerHeight) { + const lineHeight = DEFAULT_TTY_FONT_SIZE * LINE_HEIGHT_SCALE_RATIO; + const desiredHeight = tty.rows * lineHeight; + return DEFAULT_TTY_FONT_SIZE * (containerHeight / desiredHeight); + } + + return DEFAULT_TTY_FONT_SIZE; + }, [containerHeight, tty?.rows]); + + const onFit = useCallback(() => { + if (fontSize === onFitFontSize || onFitFontSize > DEFAULT_TTY_FONT_SIZE) { + onFontSizeChanged(DEFAULT_TTY_FONT_SIZE); + } else { + onFontSizeChanged(onFitFontSize); + } + }, [fontSize, onFontSizeChanged, onFitFontSize]); + + const onZoomOut = useCallback(() => { + onFontSizeChanged(Math.max(MINIMUM_FONT_SIZE, fontSize - 1)); + }, [fontSize, onFontSizeChanged]); + + const onZoomIn = useCallback(() => { + onFontSizeChanged(Math.min(MAXIMUM_FONT_SIZE, fontSize + 1)); + }, [fontSize, onFontSizeChanged]); + + return ( + + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/session_view/public/components/tty_text_sizer/translations.ts b/x-pack/plugins/session_view/public/components/tty_text_sizer/translations.ts new file mode 100644 index 0000000000000..0bec7775d43c5 --- /dev/null +++ b/x-pack/plugins/session_view/public/components/tty_text_sizer/translations.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { i18n } from '@kbn/i18n'; + +export const ZOOM_IN = i18n.translate('xpack.sessionView.zoomIn', { + defaultMessage: 'Zoom in', +}); + +export const ZOOM_FIT = i18n.translate('xpack.sessionView.zoomFit', { + defaultMessage: 'Zoom fit', +}); + +export const ZOOM_OUT = i18n.translate('xpack.sessionView.zoomOut', { + defaultMessage: 'Zoom out', +}); diff --git a/x-pack/plugins/session_view/public/methods/index.tsx b/x-pack/plugins/session_view/public/methods/index.tsx index 3654e296e7412..b8a581ea3ba95 100644 --- a/x-pack/plugins/session_view/public/methods/index.tsx +++ b/x-pack/plugins/session_view/public/methods/index.tsx @@ -7,7 +7,7 @@ import React, { lazy, Suspense } from 'react'; import { EuiLoadingSpinner } from '@elastic/eui'; -import { QueryClient, QueryClientProvider } from 'react-query'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { SessionViewDeps } from '../types'; // Initializing react-query diff --git a/x-pack/plugins/session_view/public/test/index.tsx b/x-pack/plugins/session_view/public/test/index.tsx index 6174925b6003c..244560d366ac4 100644 --- a/x-pack/plugins/session_view/public/test/index.tsx +++ b/x-pack/plugins/session_view/public/test/index.tsx @@ -8,7 +8,7 @@ import React, { memo, ReactNode, useMemo } from 'react'; import { createMemoryHistory, MemoryHistory } from 'history'; import { render as reactRender, RenderOptions, RenderResult } from '@testing-library/react'; -import { QueryClient, QueryClientProvider, setLogger } from 'react-query'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { Router } from 'react-router-dom'; import { History } from 'history'; import useObservable from 'react-use/lib/useObservable'; @@ -20,15 +20,6 @@ import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; type UiRender = (ui: React.ReactElement, options?: RenderOptions) => RenderResult; -// hide react-query output in console -setLogger({ - error: () => {}, - // eslint-disable-next-line no-console - log: console.log, - // eslint-disable-next-line no-console - warn: console.warn, -}); - /** * Mocked app root context renderer */ @@ -113,6 +104,14 @@ export const createAppRootMockRenderer = (): AppContextTestRender => { cacheTime: Infinity, }, }, + // hide react-query output in console + logger: { + error: () => {}, + // eslint-disable-next-line no-console + log: console.log, + // eslint-disable-next-line no-console + warn: console.warn, + }, }); const AppWrapper: React.FC<{ children: React.ReactElement }> = ({ children }) => ( diff --git a/x-pack/plugins/stack_alerts/common/build_sorted_events_query.ts b/x-pack/plugins/stack_alerts/common/build_sorted_events_query.ts index de9e2a32e5c27..480bbd7e64e48 100644 --- a/x-pack/plugins/stack_alerts/common/build_sorted_events_query.ts +++ b/x-pack/plugins/stack_alerts/common/build_sorted_events_query.ts @@ -21,6 +21,8 @@ export interface BuildSortedEventsQuery extends BuildSortedEventsQueryOpts { sortOrder?: 'asc' | 'desc'; searchAfterSortId: string | number | undefined; timeField: string; + fields?: string[]; + runtime_mappings?: unknown; } export const buildSortedEventsQuery = ({ @@ -35,6 +37,9 @@ export const buildSortedEventsQuery = ({ timeField, // eslint-disable-next-line @typescript-eslint/naming-convention track_total_hits, + fields, + // eslint-disable-next-line @typescript-eslint/naming-convention + runtime_mappings, }: BuildSortedEventsQuery): ESSearchRequest => { const sortField = timeField; const docFields = [timeField].map((tstamp) => ({ @@ -82,6 +87,8 @@ export const buildSortedEventsQuery = ({ }, ], }, + ...(runtime_mappings ? { runtime_mappings } : {}), + ...(fields ? { fields } : {}), }; if (searchAfterSortId) { diff --git a/x-pack/plugins/stack_alerts/server/alert_types/es_query/lib/fetch_es_query.ts b/x-pack/plugins/stack_alerts/server/alert_types/es_query/lib/fetch_es_query.ts index 97acd15416689..6a954a8efb396 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/es_query/lib/fetch_es_query.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/es_query/lib/fetch_es_query.ts @@ -26,13 +26,18 @@ export async function fetchEsQuery( ) { const { scopedClusterClient, logger } = services; const esClient = scopedClusterClient.asCurrentUser; - const { parsedQuery, dateStart, dateEnd } = getSearchParams(params); + const { + // eslint-disable-next-line @typescript-eslint/naming-convention + parsedQuery: { query, fields, runtime_mappings }, + dateStart, + dateEnd, + } = getSearchParams(params); const filter = timestamp ? { bool: { filter: [ - parsedQuery.query, + query, { bool: { must_not: [ @@ -56,9 +61,9 @@ export async function fetchEsQuery( ], }, } - : parsedQuery.query; + : query; - const query = buildSortedEventsQuery({ + const sortedQuery = buildSortedEventsQuery({ index: params.index, from: dateStart, to: dateEnd, @@ -68,11 +73,15 @@ export async function fetchEsQuery( searchAfterSortId: undefined, timeField: params.timeField, track_total_hits: true, + fields, + runtime_mappings, }); - logger.debug(`es query rule ${ES_QUERY_ID}:${ruleId} "${name}" query - ${JSON.stringify(query)}`); + logger.debug( + `es query rule ${ES_QUERY_ID}:${ruleId} "${name}" query - ${JSON.stringify(sortedQuery)}` + ); - const { body: searchResult } = await esClient.search(query, { meta: true }); + const { body: searchResult } = await esClient.search(sortedQuery, { meta: true }); logger.debug( ` es query rule ${ES_QUERY_ID}:${ruleId} "${name}" result - ${JSON.stringify(searchResult)}` diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/routes.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/routes.tsx index 9356b55ce5eb2..8112e9f19d982 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/routes.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/routes.tsx @@ -7,7 +7,6 @@ import { EuiThemeComputed } from '@elastic/eui/src/services/theme/types'; import React, { FC, useEffect } from 'react'; -import { tint } from 'polished'; import { EuiPageTemplateProps, EuiFlexGroup, EuiFlexItem, useEuiTheme } from '@elastic/eui'; import { Route, Switch, useHistory } from 'react-router-dom'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -158,17 +157,7 @@ const getRoutes = ( ), dataTestSubj: 'syntheticsMonitorManagementPage', - paddingSize: 'none', - pageBodyProps: { - style: { backgroundColor: tint(0.5, euiTheme.colors.body) }, - }, - pageContentProps: { - paddingSize: 'l', - style: { backgroundColor: euiTheme.colors.ghost }, - }, pageHeader: { - paddingSize: 'l', - style: { margin: 0 }, pageTitle: , tabs: [ { diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx index 3058aaacb4d85..1eef87ed9efcd 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx @@ -7,7 +7,7 @@ import React, { useContext, useEffect, useState } from 'react'; import useIntersection from 'react-use/lib/useIntersection'; -import styled from 'styled-components'; +import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import { useInProgressImage } from './use_in_progress_image'; @@ -23,11 +23,9 @@ import { StepImageCaption } from './step_image_caption'; import { StepImagePopover } from './step_image_popover'; import { formatCaptionContent } from './translations'; -const StepDiv = styled.div` - figure.euiImage { - div.stepArrowsFullScreen { - display: none; - } +const StepDiv = euiStyled.div` + figcaption { + display: none; } `; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.tsx index 39b1a40570db2..511253b578a31 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.tsx @@ -7,23 +7,19 @@ import { EuiImage, EuiPopover } from '@elastic/eui'; import React from 'react'; -import styled from 'styled-components'; +import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { LoadingImageState } from './no_image_available'; -import { ScreenshotRefImageData } from '../../../../../../../common/runtime_types/ping/synthetics'; +import { ScreenshotRefImageData } from '../../../../../../../common/runtime_types'; import { fullSizeImageAlt } from './translations'; import { useCompositeImage } from '../../../../../hooks/use_composite_image'; const POPOVER_IMG_HEIGHT = 360; const POPOVER_IMG_WIDTH = 640; -const StepImage = styled(EuiImage)` - &&& { - display: flex; - figure.euiImageFullScreenWrapper { +const StepImage = euiStyled(EuiImage)` + &&& .euiImageFullScreenWrapper { + figcaption { display: flex; - div.stepArrowsFullScreen { - display: flex; - } } } `; diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/settings_bottom_bar.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/settings_bottom_bar.tsx index b080c3ea89712..99d672b24dd74 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/settings_bottom_bar.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/settings_bottom_bar.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { OutPortal, createPortalNode, InPortal } from 'react-reverse-portal'; +import { OutPortal, createHtmlPortalNode, InPortal } from 'react-reverse-portal'; import { SettingsActions, SettingsActionsProps } from './settings_actions'; export const SettingsBottomBar = () => { @@ -24,4 +24,4 @@ export const SettingsActionBarPortal = (props: SettingsActionsProps) => { ); }; -export const SettingsBarPortalNode = createPortalNode(); +export const SettingsBarPortalNode = createHtmlPortalNode(); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/hooks/__snapshots__/use_url_params.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/__snapshots__/use_url_params.test.tsx.snap index 5bac7ff7caf76..febc67e115ef9 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/hooks/__snapshots__/use_url_params.test.tsx.snap +++ b/x-pack/plugins/synthetics/public/legacy_uptime/hooks/__snapshots__/use_url_params.test.tsx.snap @@ -148,6 +148,7 @@ exports[`useUrlParams deletes keys that do not have truthy values 1`] = ` Array [], Array [], Array [], + Array [], ], "results": Array [ Object { @@ -174,6 +175,12 @@ exports[`useUrlParams deletes keys that do not have truthy values 1`] = ` "selectedFilters": null, }, }, + Object { + "type": "return", + "value": Object { + "selectedFilters": null, + }, + }, ], }, "replaceReducer": [MockFunction], @@ -378,6 +385,7 @@ exports[`useUrlParams gets the expected values using the context 1`] = ` Array [], Array [], Array [], + Array [], ], "results": Array [ Object { @@ -404,6 +412,12 @@ exports[`useUrlParams gets the expected values using the context 1`] = ` "selectedFilters": null, }, }, + Object { + "type": "return", + "value": Object { + "selectedFilters": null, + }, + }, ], }, "replaceReducer": [MockFunction], diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/portals.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/portals.tsx index 2ca83d1f6972f..b4574d2a5c6d3 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/portals.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/pages/monitor_management/portals.tsx @@ -5,10 +5,10 @@ * 2.0. */ -import { createPortalNode } from 'react-reverse-portal'; +import { createHtmlPortalNode } from 'react-reverse-portal'; -export const ActionBarPortalNode = createPortalNode(); +export const ActionBarPortalNode = createHtmlPortalNode(); -export const APIKeysPortalNode = createPortalNode(); +export const APIKeysPortalNode = createHtmlPortalNode(); -export const ManageLocationsPortalNode = createPortalNode(); +export const ManageLocationsPortalNode = createHtmlPortalNode(); diff --git a/x-pack/plugins/task_manager/server/task_store.test.ts b/x-pack/plugins/task_manager/server/task_store.test.ts index e7696102db8ca..561d4ac6a0989 100644 --- a/x-pack/plugins/task_manager/server/task_store.test.ts +++ b/x-pack/plugins/task_manager/server/task_store.test.ts @@ -15,20 +15,15 @@ import { TaskLifecycleResult, SerializedConcreteTaskInstance, } from './task'; -import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; +import { elasticsearchServiceMock, savedObjectsServiceMock } from '@kbn/core/server/mocks'; import { TaskStore, SearchOpts, AggregationOpts } from './task_store'; import { savedObjectsRepositoryMock } from '@kbn/core/server/mocks'; -import { - SavedObjectsSerializer, - SavedObjectTypeRegistry, - SavedObjectAttributes, - SavedObjectsErrorHelpers, -} from '@kbn/core/server'; +import { SavedObjectAttributes, SavedObjectsErrorHelpers } from '@kbn/core/server'; import { TaskTypeDictionary } from './task_type_dictionary'; import { mockLogger } from './test_utils'; const savedObjectsClient = savedObjectsRepositoryMock.create(); -const serializer = new SavedObjectsSerializer(new SavedObjectTypeRegistry()); +const serializer = savedObjectsServiceMock.createSerializer(); beforeEach(() => jest.resetAllMocks()); diff --git a/x-pack/plugins/threat_intelligence/common/constants.ts b/x-pack/plugins/threat_intelligence/common/constants.ts index eba5c281652a9..952323223f352 100644 --- a/x-pack/plugins/threat_intelligence/common/constants.ts +++ b/x-pack/plugins/threat_intelligence/common/constants.ts @@ -7,8 +7,6 @@ export const EMPTY_VALUE = '-'; -export const DEFAULT_THREAT_INDEX_KEY = 'securitySolution:defaultThreatIndex' as const; - export const DEFAULT_DATE_FORMAT = 'dateFormat' as const; export const DEFAULT_DATE_FORMAT_TZ = 'dateFormat:tz' as const; diff --git a/x-pack/plugins/threat_intelligence/cypress/integration/indicators/indicators.spec.ts b/x-pack/plugins/threat_intelligence/cypress/integration/indicators/indicators.spec.ts index fa17dcac2d5d4..70dbd9e9fbf61 100644 --- a/x-pack/plugins/threat_intelligence/cypress/integration/indicators/indicators.spec.ts +++ b/x-pack/plugins/threat_intelligence/cypress/integration/indicators/indicators.spec.ts @@ -23,20 +23,18 @@ import { BREADCRUMBS, LEADING_BREADCRUMB, ENDING_BREADCRUMB, + FIELD_BROWSER, + FIELD_BROWSER_MODAL, } from '../../screens/indicators'; import { login } from '../../tasks/login'; import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver'; +import { selectRange } from '../../tasks/select_range'; before(() => { login(); }); -/** - * Time range extended to 15 years back to ensure fixtures are showing up correctly - * TODO: https://github.com/elastic/security-team/issues/4595 - */ -const THREAT_INTELLIGENCE_15Y_DATA = - '/app/security/threat_intelligence/indicators?indicators=(filterQuery:(language:kuery,query:%27%27),filters:!(),timeRange:(from:now-15y/d,to:now))'; +const THREAT_INTELLIGENCE = '/app/security/threat_intelligence/indicators'; const URL_WITH_CONTRADICTORY_FILTERS = '/app/security/threat_intelligence/indicators?indicators=(filterQuery:(language:kuery,query:%27%27),filters:!((%27$state%27:(store:appState),meta:(alias:!n,disabled:!f,index:%27%27,key:threat.indicator.type,negate:!f,params:(query:file),type:phrase),query:(match_phrase:(threat.indicator.type:file))),(%27$state%27:(store:appState),meta:(alias:!n,disabled:!f,index:%27%27,key:threat.indicator.type,negate:!f,params:(query:url),type:phrase),query:(match_phrase:(threat.indicator.type:url)))),timeRange:(from:now/d,to:now/d))'; @@ -51,7 +49,9 @@ describe('Indicators', () => { describe('Indicators page basics', () => { before(() => { - cy.visit(THREAT_INTELLIGENCE_15Y_DATA); + cy.visit(THREAT_INTELLIGENCE); + + selectRange(); }); it('should render the basic page elements', () => { @@ -90,7 +90,9 @@ describe('Indicators', () => { describe('Indicator page search', () => { before(() => { - cy.visit(THREAT_INTELLIGENCE_15Y_DATA); + cy.visit(THREAT_INTELLIGENCE); + + selectRange(); }); it('should narrow the results to url indicators when respective KQL search is executed', () => { @@ -121,10 +123,12 @@ describe('Indicators', () => { }); describe('No items match search criteria', () => { - before(() => + before(() => { // Contradictory filter set - cy.visit(URL_WITH_CONTRADICTORY_FILTERS) - ); + cy.visit(URL_WITH_CONTRADICTORY_FILTERS); + + selectRange(); + }); it('should not display the table when contractictory filters are set', () => { cy.get(FLYOUT_TABLE).should('not.exist'); @@ -147,4 +151,20 @@ describe('Indicators', () => { cy.get(`${FIELD_SELECTOR}`).should('have.value', threatIndicatorIp); }); }); + + describe('Field browser', () => { + before(() => { + cy.visit(THREAT_INTELLIGENCE); + + selectRange(); + }); + + describe('when field browser is triggered', () => { + it('should render proper modal window', () => { + cy.get(FIELD_BROWSER).last().click({ force: true }); + + cy.get(FIELD_BROWSER_MODAL).should('be.visible'); + }); + }); + }); }); diff --git a/x-pack/plugins/threat_intelligence/cypress/screens/indicators.ts b/x-pack/plugins/threat_intelligence/cypress/screens/indicators.ts index 3c68050c58f8c..f5ab5f46f687d 100644 --- a/x-pack/plugins/threat_intelligence/cypress/screens/indicators.ts +++ b/x-pack/plugins/threat_intelligence/cypress/screens/indicators.ts @@ -38,3 +38,11 @@ export const TABLE_CONTROLS = '[data-test-sub="dataGridControls"]'; export const INDICATOR_TYPE_CELL = '[data-gridcell-column-id="threat.indicator.type"]'; export const FIELD_SELECTOR = '[data-test-subj="tiIndicatorFieldSelectorDropdown"]'; + +export const FIELD_BROWSER = `[data-test-subj="show-field-browser"]`; + +export const FIELD_BROWSER_MODAL = `[data-test-subj="fields-browser-container"]`; + +export const FIELD_BROWSER_MODAL_SOURCE_CHECKBOX = `[data-test-subj="field-_source-checkbox"]`; + +export const FIELD_BROWSER_CLOSE = `[data-test-subj="close"]`; diff --git a/x-pack/plugins/threat_intelligence/cypress/tasks/select_range.ts b/x-pack/plugins/threat_intelligence/cypress/tasks/select_range.ts new file mode 100644 index 0000000000000..8bf94c7f920ee --- /dev/null +++ b/x-pack/plugins/threat_intelligence/cypress/tasks/select_range.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FIELD_BROWSER, TIME_RANGE_PICKER } from '../screens/indicators'; + +export const selectRange = () => { + cy.get(FIELD_BROWSER); + + cy.get(TIME_RANGE_PICKER).first().click({ force: true }); + cy.get('[aria-label="Time unit"]').select('y'); + cy.get('[data-test-subj="superDatePickerQuickMenu"] .euiQuickSelect__applyButton').click({ + force: true, + }); +}; diff --git a/x-pack/plugins/threat_intelligence/kibana.json b/x-pack/plugins/threat_intelligence/kibana.json index 18493feca7c4f..efd1e8bce761e 100644 --- a/x-pack/plugins/threat_intelligence/kibana.json +++ b/x-pack/plugins/threat_intelligence/kibana.json @@ -3,6 +3,7 @@ "version": "1.0.0", "kibanaVersion": "kibana", "ui": true, + "server": false, "owner": { "name": "Protections Experience Team", "githubTeam": "protections-experience" @@ -14,7 +15,8 @@ "unifiedSearch", "kibanaUtils", "navigation", - "kibanaReact" + "kibanaReact", + "triggersActionsUi" ], "requiredBundles": [ "data", diff --git a/x-pack/plugins/threat_intelligence/public/common/mocks/mock_kibana_data_service.tsx b/x-pack/plugins/threat_intelligence/public/common/mocks/mock_kibana_data_service.tsx deleted file mode 100644 index c002343369988..0000000000000 --- a/x-pack/plugins/threat_intelligence/public/common/mocks/mock_kibana_data_service.tsx +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { Observable } from 'rxjs'; -import { TimeRangeBounds } from '@kbn/data-plugin/common'; -import * as hook from '../../hooks/use_kibana'; - -export interface MockSearchServiceParams { - searchSubject?: Observable; - calculateSubject?: TimeRangeBounds; -} - -export const mockKibanaDataService = ({ - searchSubject, - calculateSubject, -}: MockSearchServiceParams) => { - const search = jest.fn().mockReturnValue(searchSubject); - const showError = jest.fn(); - const getUiSetting = jest.fn(); - const calculateBounds = jest.fn().mockReturnValue(calculateSubject); - - (hook as jest.Mocked).useKibana.mockReturnValue({ - services: { - data: { - search: { search, showError }, - query: { timefilter: { timefilter: { calculateBounds } } }, - }, - uiSettings: { get: getUiSetting }, - }, - } as any); - - return { - search, - showError, - getUiSetting, - calculateBounds, - }; -}; diff --git a/x-pack/plugins/threat_intelligence/public/common/mocks/mock_security_context.tsx b/x-pack/plugins/threat_intelligence/public/common/mocks/mock_security_context.tsx new file mode 100644 index 0000000000000..666fc6318c577 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/common/mocks/mock_security_context.tsx @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { SecuritySolutionPluginContext } from '../..'; + +export const getSecuritySolutionContextMock = (): SecuritySolutionPluginContext => ({ + getFiltersGlobalComponent: + () => + ({ children }) => +
{children}
, + licenseService: { + isEnterprise() { + return true; + }, + }, + sourcererDataView: { + browserFields: {}, + selectedPatterns: [], + indexPattern: { fields: [], title: '' }, + }, +}); diff --git a/x-pack/plugins/threat_intelligence/public/common/mocks/mock_use_kibana_for_filters.ts b/x-pack/plugins/threat_intelligence/public/common/mocks/mock_use_kibana_for_filters.ts index cb77e0cec5523..598bec07732a0 100644 --- a/x-pack/plugins/threat_intelligence/public/common/mocks/mock_use_kibana_for_filters.ts +++ b/x-pack/plugins/threat_intelligence/public/common/mocks/mock_use_kibana_for_filters.ts @@ -42,7 +42,6 @@ export const mockUseKibanaForFilters = ({ }, }, dataViews: { getFieldsForWildcard }, - uiSettings: { get: () => ['mock-index'] }, }, } as any); diff --git a/x-pack/plugins/threat_intelligence/public/common/mocks/story_providers.tsx b/x-pack/plugins/threat_intelligence/public/common/mocks/story_providers.tsx new file mode 100644 index 0000000000000..752e93b756ddb --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/common/mocks/story_providers.tsx @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { ReactNode, VFC } from 'react'; +import { createKibanaReactContext } from '@kbn/kibana-react-plugin/public'; +import { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import { CoreStart, IUiSettingsClient } from '@kbn/core/public'; +import { SecuritySolutionContext } from '../../containers/security_solution_context'; +import { getSecuritySolutionContextMock } from './mock_security_context'; + +export interface KibanaContextMock { + /** + * For the data plugin (see {@link DataPublicPluginStart}) + */ + data?: DataPublicPluginStart; + /** + * For the core ui-settings package (see {@link IUiSettingsClient}) + */ + uiSettings?: IUiSettingsClient; +} + +export interface StoryProvidersComponentProps { + /** + * Used to generate a new KibanaReactContext (using {@link createKibanaReactContext}) + */ + kibana: KibanaContextMock; + /** + * Component(s) to be displayed inside + */ + children: ReactNode; +} + +/** + * Helper functional component used in Storybook stories. + * Wraps the story with our {@link SecuritySolutionContext} and KibanaReactContext. + */ +export const StoryProvidersComponent: VFC = ({ + children, + kibana, +}) => { + const KibanaReactContext = createKibanaReactContext(kibana as CoreStart); + const securitySolutionContextMock = getSecuritySolutionContextMock(); + + return ( + + {children} + + ); +}; diff --git a/x-pack/plugins/threat_intelligence/public/common/mocks/test_providers.tsx b/x-pack/plugins/threat_intelligence/public/common/mocks/test_providers.tsx index 019587c4b30fb..a2c0318c482aa 100644 --- a/x-pack/plugins/threat_intelligence/public/common/mocks/test_providers.tsx +++ b/x-pack/plugins/threat_intelligence/public/common/mocks/test_providers.tsx @@ -13,9 +13,11 @@ import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import type { IStorage } from '@kbn/kibana-utils-plugin/public'; import { Storage } from '@kbn/kibana-utils-plugin/public'; import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; +import { BehaviorSubject } from 'rxjs'; +import { getSecuritySolutionContextMock } from './mock_security_context'; import { mockUiSetting } from './mock_kibana_ui_setting'; -import { KibanaContextProvider } from '../../hooks/use_kibana'; -import { Services, ThreatIntelligenceSecuritySolutionContext } from '../../types'; +import { KibanaContext } from '../../hooks/use_kibana'; +import { SecuritySolutionPluginContext } from '../../types'; import { SecuritySolutionContext } from '../../containers/security_solution_context'; export const localStorageMock = (): IStorage => { @@ -51,6 +53,7 @@ export const unifiedSearch = unifiedSearchPluginMock.createStartContract(); const validDate: string = '1 Jan 2022 00:00:00 GMT'; const data = dataPluginMock.createStartContract(); + const dataServiceMock = { ...data, query: { @@ -83,20 +86,7 @@ const dataServiceMock = { }, search: { ...data.search, - search: jest.fn().mockImplementation(() => ({ - subscribe: jest.fn().mockImplementation(() => ({ - error: jest.fn(), - next: jest.fn(), - unsubscribe: jest.fn(), - })), - pipe: jest.fn().mockImplementation(() => ({ - subscribe: jest.fn().mockImplementation(() => ({ - error: jest.fn(), - next: jest.fn(), - unsubscribe: jest.fn(), - })), - })), - })), + search: jest.fn().mockReturnValue(new BehaviorSubject({})), }, }; @@ -106,29 +96,31 @@ const coreServiceMock = { uiSettings: { get: jest.fn().mockImplementation(mockUiSetting) }, }; -const mockSecurityContext: ThreatIntelligenceSecuritySolutionContext = { - getFiltersGlobalComponent: - () => - ({ children }) => -
{children}
, - licenseService: { - isEnterprise() { - return true; - }, - }, -}; +const mockSecurityContext: SecuritySolutionPluginContext = getSecuritySolutionContextMock(); -const mockedServices = { +export const mockedServices = { ...coreServiceMock, data: dataServiceMock, storage, unifiedSearch, -} as unknown as Services; + triggersActionsUi: { + getFieldBrowser: jest.fn().mockReturnValue(null), + }, +}; export const TestProvidersComponent: FC = ({ children }) => ( - + {children} - + ); + +export type MockedSearch = jest.Mocked; +export type MockedTimefilter = jest.Mocked; +export type MockedTriggersActionsUi = jest.Mocked; + +export const mockedSearchService = mockedServices.data.search as MockedSearch; +export const mockedTimefilterService = mockedServices.data.query.timefilter as MockedTimefilter; +export const mockedTriggersActionsUiService = + mockedServices.triggersActionsUi as MockedTriggersActionsUi; diff --git a/x-pack/plugins/threat_intelligence/public/components/paywall/index.tsx b/x-pack/plugins/threat_intelligence/public/components/paywall/index.tsx new file mode 100644 index 0000000000000..46d2df81a1c8c --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/components/paywall/index.tsx @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './paywall'; diff --git a/x-pack/plugins/threat_intelligence/public/components/paywall/paywall.stories.tsx b/x-pack/plugins/threat_intelligence/public/components/paywall/paywall.stories.tsx new file mode 100644 index 0000000000000..14de7d5b907fb --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/components/paywall/paywall.stories.tsx @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { Paywall } from './paywall'; + +export default { + component: BasicPaywall, + title: 'Paywall', +}; + +export function BasicPaywall() { + return ; +} diff --git a/x-pack/plugins/threat_intelligence/public/components/paywall/paywall.tsx b/x-pack/plugins/threat_intelligence/public/components/paywall/paywall.tsx new file mode 100644 index 0000000000000..8f095a2fd9baa --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/components/paywall/paywall.tsx @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { VFC } from 'react'; +import { + EuiButton, + EuiButtonEmpty, + EuiEmptyPrompt, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +interface PaywallProps { + /** + * Can be obtained using `http.basePath.prepend('/app/management/stack/license_management')` + */ + licenseManagementHref: string; +} + +export const Paywall: VFC = ({ licenseManagementHref }) => { + return ( + } + color="subdued" + data-test-subj="tiPaywall" + title={ +

+ +

+ } + body={ +

+ +

+ } + actions={ + + +
+ + + +
+
+ +
+ + + +
+
+
+ } + /> + ); +}; diff --git a/x-pack/plugins/threat_intelligence/public/containers/enterprise_guard/enterprise_guard.test.tsx b/x-pack/plugins/threat_intelligence/public/containers/enterprise_guard/enterprise_guard.test.tsx index bb4739bb6251f..a01c3b94e1cd4 100644 --- a/x-pack/plugins/threat_intelligence/public/containers/enterprise_guard/enterprise_guard.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/containers/enterprise_guard/enterprise_guard.test.tsx @@ -7,7 +7,8 @@ import { render, screen } from '@testing-library/react'; import React from 'react'; -import { ThreatIntelligenceSecuritySolutionContext } from '../../types'; +import { TestProvidersComponent } from '../../common/mocks/test_providers'; +import { SecuritySolutionPluginContext } from '../../types'; import { SecuritySolutionContext } from '../security_solution_context'; import { EnterpriseGuard } from './enterprise_guard'; @@ -15,17 +16,19 @@ describe('', () => { describe('when on enterprise plan', () => { it('should render specified children', () => { render( - - -
enterprise only content
-
-
+ + + +
enterprise only content
+
+
+
); expect(screen.queryByText('enterprise only content')).toBeInTheDocument(); @@ -35,21 +38,23 @@ describe('', () => { describe('when not on enterprise plan', () => { it('should render specified children', () => { render( - - fallback for non enterprise
}> -
enterprise only content
- - + + + +
enterprise only content
+
+
+
); expect(screen.queryByText('enterprise only content')).not.toBeInTheDocument(); - expect(screen.queryByText('fallback for non enterprise')).toBeInTheDocument(); + expect(screen.queryByTestId('tiPaywall')).toBeInTheDocument(); }); }); }); diff --git a/x-pack/plugins/threat_intelligence/public/containers/enterprise_guard/enterprise_guard.tsx b/x-pack/plugins/threat_intelligence/public/containers/enterprise_guard/enterprise_guard.tsx index 67351c1e3d149..06ca98973af09 100644 --- a/x-pack/plugins/threat_intelligence/public/containers/enterprise_guard/enterprise_guard.tsx +++ b/x-pack/plugins/threat_intelligence/public/containers/enterprise_guard/enterprise_guard.tsx @@ -5,20 +5,25 @@ * 2.0. */ -import React, { ReactElement } from 'react'; +import React from 'react'; import { FC } from 'react'; +import { Paywall } from '../../components/paywall'; +import { useKibana } from '../../hooks/use_kibana'; import { useSecurityContext } from '../../hooks/use_security_context'; -interface EnterpriseGuardProps { - fallback?: ReactElement; -} - -export const EnterpriseGuard: FC = ({ children, fallback = null }) => { +export const EnterpriseGuard: FC = ({ children }) => { const { licenseService } = useSecurityContext(); + const { + services: { http }, + } = useKibana(); if (licenseService.isEnterprise()) { return <>{children}; } - return fallback; + return ( + + ); }; diff --git a/x-pack/plugins/threat_intelligence/public/containers/security_solution_context.tsx b/x-pack/plugins/threat_intelligence/public/containers/security_solution_context.tsx index 0dae7e9538c38..91a2934463d07 100644 --- a/x-pack/plugins/threat_intelligence/public/containers/security_solution_context.tsx +++ b/x-pack/plugins/threat_intelligence/public/containers/security_solution_context.tsx @@ -6,8 +6,8 @@ */ import { createContext } from 'react'; -import { ThreatIntelligenceSecuritySolutionContext } from '../types'; +import { SecuritySolutionPluginContext } from '../types'; -export const SecuritySolutionContext = createContext< - ThreatIntelligenceSecuritySolutionContext | undefined ->(undefined); +export const SecuritySolutionContext = createContext( + undefined +); diff --git a/x-pack/plugins/threat_intelligence/public/hooks/use_kibana.ts b/x-pack/plugins/threat_intelligence/public/hooks/use_kibana.ts index 28e15734e6dcc..a6431c3c40a8f 100644 --- a/x-pack/plugins/threat_intelligence/public/hooks/use_kibana.ts +++ b/x-pack/plugins/threat_intelligence/public/hooks/use_kibana.ts @@ -5,9 +5,13 @@ * 2.0. */ -import { KibanaContextProvider, useKibana } from '@kbn/kibana-react-plugin/public'; +import { + KibanaContextProvider, + useKibana, + context as KibanaContext, +} from '@kbn/kibana-react-plugin/public'; import { Services } from '../types'; const useTypedKibana = () => useKibana(); -export { KibanaContextProvider, useTypedKibana as useKibana }; +export { KibanaContextProvider, useTypedKibana as useKibana, KibanaContext }; diff --git a/x-pack/plugins/threat_intelligence/public/hooks/use_security_context.ts b/x-pack/plugins/threat_intelligence/public/hooks/use_security_context.ts index 433f12bf548c0..4d5fad34baf09 100644 --- a/x-pack/plugins/threat_intelligence/public/hooks/use_security_context.ts +++ b/x-pack/plugins/threat_intelligence/public/hooks/use_security_context.ts @@ -7,9 +7,9 @@ import { useContext } from 'react'; import { SecuritySolutionContext } from '../containers/security_solution_context'; -import { ThreatIntelligenceSecuritySolutionContext } from '../types'; +import { SecuritySolutionPluginContext } from '../types'; -export const useSecurityContext = (): ThreatIntelligenceSecuritySolutionContext => { +export const useSecurityContext = (): SecuritySolutionPluginContext => { const contextValue = useContext(SecuritySolutionContext); if (!contextValue) { diff --git a/x-pack/plugins/threat_intelligence/public/index.ts b/x-pack/plugins/threat_intelligence/public/index.ts index 3751d0d0f7003..539b807426341 100755 --- a/x-pack/plugins/threat_intelligence/public/index.ts +++ b/x-pack/plugins/threat_intelligence/public/index.ts @@ -24,5 +24,5 @@ export function plugin() { export type { ThreatIntelligencePluginSetup, ThreatIntelligencePluginStart, - ThreatIntelligenceSecuritySolutionContext, + SecuritySolutionPluginContext, } from './types'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.stories.tsx index cda6671d03fda..478b2ec398b97 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.stories.tsx @@ -7,12 +7,14 @@ import moment from 'moment'; import React from 'react'; +import { MemoryRouter } from 'react-router-dom'; import { of } from 'rxjs'; import { Story } from '@storybook/react'; import { DataView, DataViewField } from '@kbn/data-views-plugin/common'; -import { createKibanaReactContext } from '@kbn/kibana-react-plugin/public'; -import { CoreStart } from '@kbn/core/public'; import { TimeRange } from '@kbn/es-query'; +import { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import { IUiSettingsClient } from '@kbn/core/public'; +import { StoryProvidersComponent } from '../../../../common/mocks/story_providers'; import { Aggregation, AGGREGATION_NAME } from '../../hooks/use_aggregated_indicators'; import { DEFAULT_TIME_RANGE } from '../../hooks/use_filters/utils'; import { IndicatorsBarChartWrapper } from './indicators_barchart_wrapper'; @@ -23,20 +25,18 @@ export default { }; const mockTimeRange: TimeRange = DEFAULT_TIME_RANGE; -const mockIndexPatterns: DataView[] = [ - { - fields: [ - { - name: '@timestamp', - type: 'date', - } as DataViewField, - { - name: 'threat.feed.name', - type: 'string', - } as DataViewField, - ], - } as DataView, -]; +const mockIndexPattern: DataView = { + fields: [ + { + name: '@timestamp', + type: 'date', + } as DataViewField, + { + name: 'threat.feed.name', + type: 'string', + } as DataViewField, + ], +} as DataView; const validDate: string = '1 Jan 2022 00:00:00 GMT'; const numberOfDays: number = 1; @@ -76,38 +76,43 @@ const aggregation2: Aggregation = { doc_count: 0, key: '[Filebeat] AbuseCH MalwareBazaar', }; -const KibanaReactContext = createKibanaReactContext({ - data: { - search: { - search: () => - of({ - rawResponse: { - aggregations: { - [AGGREGATION_NAME]: { - buckets: [aggregation1, aggregation2], - }, +const mockData = { + search: { + search: () => + of({ + rawResponse: { + aggregations: { + [AGGREGATION_NAME]: { + buckets: [aggregation1, aggregation2], }, }, - }), - }, - query: { - timefilter: { - timefilter: { - calculateBounds: () => ({ - min: moment(validDate), - max: moment(validDate).add(numberOfDays, 'days'), - }), }, + }), + }, + query: { + timefilter: { + timefilter: { + calculateBounds: () => ({ + min: moment(validDate), + max: moment(validDate).add(numberOfDays, 'days'), + }), }, }, + filterManager: { + getFilters: () => {}, + setFilters: () => {}, + getUpdates$: () => of(), + }, }, - uiSettings: { get: () => {} }, -} as unknown as Partial); +} as unknown as DataPublicPluginStart; + +const mockUiSettings = { get: () => {} } as unknown as IUiSettingsClient; export const Default: Story = () => { return ( - - - + + + ); }; +Default.decorators = [(story) => {story()}]; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.test.tsx index 39afea8a11a0e..9deeb4e5bcb8a 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.test.tsx @@ -12,28 +12,42 @@ import { DataView, DataViewField } from '@kbn/data-views-plugin/common'; import { TestProvidersComponent } from '../../../../common/mocks/test_providers'; import { IndicatorsBarChartWrapper } from './indicators_barchart_wrapper'; import { DEFAULT_TIME_RANGE } from '../../hooks/use_filters/utils'; +import { useFilters } from '../../hooks/use_filters'; + +jest.mock('../../hooks/use_filters'); + +const mockIndexPattern: DataView = { + fields: [ + { + name: '@timestamp', + type: 'date', + } as DataViewField, + { + name: 'threat.feed.name', + type: 'string', + } as DataViewField, + ], +} as DataView; -const mockIndexPatterns: DataView[] = [ - { - fields: [ - { - name: '@timestamp', - type: 'date', - } as DataViewField, - { - name: 'threat.feed.name', - type: 'string', - } as DataViewField, - ], - } as DataView, -]; const mockTimeRange: TimeRange = DEFAULT_TIME_RANGE; +const stub = () => {}; + describe('', () => { + beforeEach(() => { + (useFilters as jest.MockedFunction).mockReturnValue({ + filters: [], + filterQuery: { language: 'kuery', query: '' }, + filterManager: {} as any, + handleSavedQuery: stub, + handleSubmitQuery: stub, + handleSubmitTimeRange: stub, + }); + }); it('should render barchart and field selector dropdown', () => { const component = render( - + ); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.tsx index 0988e50dc0007..a30bdffe0e23c 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_barchart_wrapper/indicators_barchart_wrapper.tsx @@ -8,8 +8,8 @@ import React, { memo } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { DataView } from '@kbn/data-views-plugin/common'; import { TimeRange } from '@kbn/es-query'; +import { SecuritySolutionDataViewBase } from '../../../../types'; import { RawIndicatorFieldId } from '../../../../../common/types/indicator'; import { useAggregatedIndicators } from '../../hooks/use_aggregated_indicators'; import { IndicatorsFieldSelector } from '../indicators_field_selector/indicators_field_selector'; @@ -19,11 +19,11 @@ const DEFAULT_FIELD = RawIndicatorFieldId.Feed; export interface IndicatorsBarChartWrapperProps { timeRange?: TimeRange; - indexPatterns: DataView[]; + indexPattern: SecuritySolutionDataViewBase; } export const IndicatorsBarChartWrapper = memo( - ({ timeRange, indexPatterns }) => { + ({ timeRange, indexPattern }) => { const { dateRange, indicators, onFieldChange } = useAggregatedIndicators({ timeRange }); return ( @@ -41,7 +41,7 @@ export const IndicatorsBarChartWrapper = memo( diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_browser/index.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_browser/index.tsx new file mode 100644 index 0000000000000..8fefebf88e799 --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_browser/index.tsx @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './indicators_field_browser'; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_browser/indicators_field_browser.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_browser/indicators_field_browser.test.tsx new file mode 100644 index 0000000000000..29e97062cf22d --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_browser/indicators_field_browser.test.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 { + mockedTriggersActionsUiService, + TestProvidersComponent, +} from '../../../../common/mocks/test_providers'; +import { render } from '@testing-library/react'; +import React from 'react'; +import { IndicatorsFieldBrowser } from './indicators_field_browser'; + +const stub = jest.fn(); + +describe('', () => { + it('should retrieve the field browser widget from respective service', () => { + render( + , + { + wrapper: TestProvidersComponent, + } + ); + + expect(mockedTriggersActionsUiService.getFieldBrowser).toHaveBeenCalledTimes(1); + expect(mockedTriggersActionsUiService.getFieldBrowser).toHaveBeenCalledWith( + expect.objectContaining({ + browserFields: {}, + columnIds: [], + onResetColumns: stub, + onToggleColumn: stub, + options: {}, + }) + ); + }); +}); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_browser/indicators_field_browser.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_browser/indicators_field_browser.tsx new file mode 100644 index 0000000000000..2adcc4ee5b9ee --- /dev/null +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_browser/indicators_field_browser.tsx @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { BrowserField } from '@kbn/triggers-actions-ui-plugin/public/application/sections/field_browser/types'; +import { VFC } from 'react'; +import { useKibana } from '../../../../hooks/use_kibana'; + +export interface IndicatorsFieldBrowserProps { + browserFields: Readonly>>; + columnIds: string[]; + onResetColumns: () => void; + onToggleColumn: (columnId: string) => void; +} + +export const IndicatorsFieldBrowser: VFC = ({ + browserFields, + columnIds, + onResetColumns, + onToggleColumn, +}) => { + const { triggersActionsUi } = useKibana().services; + + return triggersActionsUi.getFieldBrowser({ + browserFields, + columnIds, + onResetColumns, + onToggleColumn, + options: {}, + }); +}; diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/indicators_field_selector.stories.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/indicators_field_selector.stories.tsx index f7bb3300334a5..94ca9f903aec6 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/indicators_field_selector.stories.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/indicators_field_selector.stories.tsx @@ -11,20 +11,18 @@ import { DataView, DataViewField } from '@kbn/data-views-plugin/common'; import { RawIndicatorFieldId } from '../../../../../common/types/indicator'; import { IndicatorsFieldSelector } from './indicators_field_selector'; -const mockIndexPatterns: DataView[] = [ - { - fields: [ - { - name: '@timestamp', - type: 'date', - } as DataViewField, - { - name: 'threat.feed.name', - type: 'string', - } as DataViewField, - ], - } as DataView, -]; +const mockIndexPattern: DataView = { + fields: [ + { + name: '@timestamp', + type: 'date', + } as DataViewField, + { + name: 'threat.feed.name', + type: 'string', + } as DataViewField, + ], +} as DataView; export default { component: IndicatorsFieldSelector, @@ -34,7 +32,7 @@ export default { export const Default: Story = () => { return ( console.log(value)} /> @@ -44,7 +42,7 @@ export const Default: Story = () => { export const WithDefaultValue: Story = () => { return ( console.log(value)} defaultStackByValue={RawIndicatorFieldId.LastSeen} @@ -55,7 +53,7 @@ export const WithDefaultValue: Story = () => { export const NoData: Story = () => { return ( console.log(value)} /> diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/indicators_field_selector.test.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/indicators_field_selector.test.tsx index 21a0f56dfb36a..e3bd4129d4fc5 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/indicators_field_selector.test.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/components/indicators_field_selector/indicators_field_selector.test.tsx @@ -11,27 +11,25 @@ import { DataView, DataViewField } from '@kbn/data-views-plugin/common'; import { TestProvidersComponent } from '../../../../common/mocks/test_providers'; import { DROPDOWN_TEST_ID, IndicatorsFieldSelector } from './indicators_field_selector'; -const mockIndexPatterns: DataView[] = [ - { - fields: [ - { - name: '@timestamp', - type: 'date', - } as DataViewField, - { - name: 'threat.feed.name', - type: 'string', - } as DataViewField, - ], - } as DataView, -]; +const mockIndexPattern: DataView = { + fields: [ + { + name: '@timestamp', + type: 'date', + } as DataViewField, + { + name: 'threat.feed.name', + type: 'string', + } as DataViewField, + ], +} as DataView; describe('', () => { it('should handle empty array of indexPatterns', () => { const component = render( console.log(value)} /> @@ -45,7 +43,7 @@ describe('', () => { const component = render( console.log(value)} /> @@ -56,7 +54,7 @@ describe('', () => { const dropdownOptions: string = component.getByTestId(DROPDOWN_TEST_ID).innerHTML; const optionsCount: number = (dropdownOptions.match(/