diff --git a/.buildkite/pipelines/serverless.yml b/.buildkite/pipelines/serverless.yml index ec09f77e751ec..f4d7d0d49469b 100644 --- a/.buildkite/pipelines/serverless.yml +++ b/.buildkite/pipelines/serverless.yml @@ -21,19 +21,6 @@ steps: - exit_status: '-1' limit: 3 - - command: SERVERLESS_ENVIRONMENT=common .buildkite/scripts/steps/functional/serverless_ftr.sh - label: 'Serverless Common Tests' - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 40 - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 - - command: SERVERLESS_ENVIRONMENT=observability .buildkite/scripts/steps/functional/serverless_ftr.sh label: 'Serverless Observability Tests' agents: diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7ed90e7930b3b..9f41080bd5a59 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -552,6 +552,7 @@ examples/response_stream @elastic/ml-ui packages/kbn-rison @elastic/kibana-operations x-pack/plugins/rollup @elastic/platform-deployment-management examples/routing_example @elastic/kibana-core +packages/kbn-rrule @elastic/response-ops packages/kbn-rule-data-utils @elastic/security-detections-response @elastic/actionable-observability @elastic/response-ops x-pack/plugins/rule_registry @elastic/response-ops @elastic/actionable-observability x-pack/plugins/runtime_fields @elastic/platform-deployment-management @@ -742,6 +743,7 @@ packages/kbn-utility-types @elastic/kibana-core packages/kbn-utility-types-jest @elastic/kibana-operations packages/kbn-utils @elastic/kibana-operations x-pack/plugins/ux @elastic/uptime +examples/v8_profiler_examples @elastic/response-ops packages/kbn-validate-next-docs-cli @elastic/kibana-operations src/plugins/vis_default_editor @elastic/kibana-visualizations src/plugins/vis_types/gauge @elastic/kibana-visualizations diff --git a/api_docs/actions.devdocs.json b/api_docs/actions.devdocs.json index cf8e0ca6a5155..c79b223c1b7f3 100644 --- a/api_docs/actions.devdocs.json +++ b/api_docs/actions.devdocs.json @@ -2412,6 +2412,17 @@ "path": "x-pack/plugins/actions/server/types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "actions", + "id": "def-server.ActionResult.isSystemAction", + "type": "boolean", + "tags": [], + "label": "isSystemAction", + "description": [], + "path": "x-pack/plugins/actions/server/types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -2651,6 +2662,20 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "actions", + "id": "def-server.ActionType.isSystemAction", + "type": "CompoundType", + "tags": [], + "label": "isSystemAction", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/actions/server/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "actions", "id": "def-server.ActionType.renderParameterTemplates", diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index e4059a185d67f..6e7c474cacd7a 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github description: API docs for the actions plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'actions'] --- import actionsObj from './actions.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-o | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 268 | 10 | 263 | 27 | +| 270 | 10 | 265 | 27 | ## Client diff --git a/api_docs/advanced_settings.mdx b/api_docs/advanced_settings.mdx index c66454a419263..fd3ff9831d794 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github description: API docs for the advancedSettings plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index 79d62cf6f7adf..ef98513eae8af 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github description: API docs for the aiops plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; diff --git a/api_docs/alerting.devdocs.json b/api_docs/alerting.devdocs.json index bff251325f574..67ed79d64cdc4 100644 --- a/api_docs/alerting.devdocs.json +++ b/api_docs/alerting.devdocs.json @@ -7144,239 +7144,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "alerting", - "id": "def-common.RRuleRecord", - "type": "Interface", - "tags": [], - "label": "RRuleRecord", - "description": [], - "path": "x-pack/plugins/alerting/common/rrule_type.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "alerting", - "id": "def-common.RRuleRecord.dtstart", - "type": "string", - "tags": [], - "label": "dtstart", - "description": [], - "path": "x-pack/plugins/alerting/common/rrule_type.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "alerting", - "id": "def-common.RRuleRecord.tzid", - "type": "string", - "tags": [], - "label": "tzid", - "description": [], - "path": "x-pack/plugins/alerting/common/rrule_type.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "alerting", - "id": "def-common.RRuleRecord.freq", - "type": "CompoundType", - "tags": [], - "label": "freq", - "description": [], - "signature": [ - "0 | 2 | 6 | 5 | 4 | 3 | 1 | undefined" - ], - "path": "x-pack/plugins/alerting/common/rrule_type.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "alerting", - "id": "def-common.RRuleRecord.until", - "type": "string", - "tags": [], - "label": "until", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "x-pack/plugins/alerting/common/rrule_type.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "alerting", - "id": "def-common.RRuleRecord.count", - "type": "number", - "tags": [], - "label": "count", - "description": [], - "signature": [ - "number | undefined" - ], - "path": "x-pack/plugins/alerting/common/rrule_type.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "alerting", - "id": "def-common.RRuleRecord.interval", - "type": "number", - "tags": [], - "label": "interval", - "description": [], - "signature": [ - "number | undefined" - ], - "path": "x-pack/plugins/alerting/common/rrule_type.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "alerting", - "id": "def-common.RRuleRecord.wkst", - "type": "CompoundType", - "tags": [], - "label": "wkst", - "description": [], - "signature": [ - "WeekdayStr", - " | undefined" - ], - "path": "x-pack/plugins/alerting/common/rrule_type.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "alerting", - "id": "def-common.RRuleRecord.byweekday", - "type": "Array", - "tags": [], - "label": "byweekday", - "description": [], - "signature": [ - "(string | number)[] | undefined" - ], - "path": "x-pack/plugins/alerting/common/rrule_type.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "alerting", - "id": "def-common.RRuleRecord.bymonth", - "type": "Array", - "tags": [], - "label": "bymonth", - "description": [], - "signature": [ - "number[] | undefined" - ], - "path": "x-pack/plugins/alerting/common/rrule_type.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "alerting", - "id": "def-common.RRuleRecord.bysetpos", - "type": "Array", - "tags": [], - "label": "bysetpos", - "description": [], - "signature": [ - "number[] | undefined" - ], - "path": "x-pack/plugins/alerting/common/rrule_type.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "alerting", - "id": "def-common.RRuleRecord.bymonthday", - "type": "Array", - "tags": [], - "label": "bymonthday", - "description": [], - "signature": [ - "number[]" - ], - "path": "x-pack/plugins/alerting/common/rrule_type.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "alerting", - "id": "def-common.RRuleRecord.byyearday", - "type": "Array", - "tags": [], - "label": "byyearday", - "description": [], - "signature": [ - "number[]" - ], - "path": "x-pack/plugins/alerting/common/rrule_type.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "alerting", - "id": "def-common.RRuleRecord.byweekno", - "type": "Array", - "tags": [], - "label": "byweekno", - "description": [], - "signature": [ - "number[]" - ], - "path": "x-pack/plugins/alerting/common/rrule_type.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "alerting", - "id": "def-common.RRuleRecord.byhour", - "type": "Array", - "tags": [], - "label": "byhour", - "description": [], - "signature": [ - "number[]" - ], - "path": "x-pack/plugins/alerting/common/rrule_type.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "alerting", - "id": "def-common.RRuleRecord.byminute", - "type": "Array", - "tags": [], - "label": "byminute", - "description": [], - "signature": [ - "number[]" - ], - "path": "x-pack/plugins/alerting/common/rrule_type.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "alerting", - "id": "def-common.RRuleRecord.bysecond", - "type": "Array", - "tags": [], - "label": "bysecond", - "description": [], - "signature": [ - "number[]" - ], - "path": "x-pack/plugins/alerting/common/rrule_type.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "alerting", "id": "def-common.Rule", @@ -10247,6 +10014,31 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-common.RRuleRecord", + "type": "Type", + "tags": [], + "label": "RRuleRecord", + "description": [], + "signature": [ + "Omit<", + "Options", + ", \"wkst\" | \"byweekday\" | \"dtstart\" | \"until\"> & { dtstart: string; byweekday?: (string | number)[] | undefined; wkst?: ", + { + "pluginId": "@kbn/rrule", + "scope": "common", + "docId": "kibKbnRrulePluginApi", + "section": "def-common.WeekdayStr", + "text": "WeekdayStr" + }, + " | undefined; until?: string | undefined; }" + ], + "path": "x-pack/plugins/alerting/common/rrule_type.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-common.RuleActionAlertsFilterProperty", diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index b0c136fe1f236..15a204ae04874 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github description: API docs for the alerting plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] --- import alertingObj from './alerting.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-o | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 635 | 1 | 611 | 47 | +| 619 | 1 | 595 | 47 | ## Client diff --git a/api_docs/apm.devdocs.json b/api_docs/apm.devdocs.json index add26e70b0bfc..7731ac7692b96 100644 --- a/api_docs/apm.devdocs.json +++ b/api_docs/apm.devdocs.json @@ -782,7 +782,13 @@ "text": "HomeServerPluginStart" }, ">; } | undefined; ml?: { setup: ", - "SharedServices", + { + "pluginId": "ml", + "scope": "server", + "docId": "kibMlPluginApi", + "section": "def-server.MlPluginSetup", + "text": "MlPluginSetup" + }, "; start: () => Promise; } | undefined; security?: { setup: ", { "pluginId": "security", diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index 9ff7b42cdc6b6..c14f27ab3bedc 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github description: API docs for the apm plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/asset_manager.mdx b/api_docs/asset_manager.mdx index 6ce00c0fb8669..e6f19ae5a0655 100644 --- a/api_docs/asset_manager.mdx +++ b/api_docs/asset_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/assetManager title: "assetManager" image: https://source.unsplash.com/400x175/?github description: API docs for the assetManager plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'assetManager'] --- import assetManagerObj from './asset_manager.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index fb9c9b73b8ae1..897a78bb219c3 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github description: API docs for the banners plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners'] --- import bannersObj from './banners.devdocs.json'; diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx index 4844ba6bce50a..44c2cbb782545 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github description: API docs for the bfetch plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'bfetch'] --- import bfetchObj from './bfetch.devdocs.json'; diff --git a/api_docs/canvas.mdx b/api_docs/canvas.mdx index c6f7616af2ace..75b92e187bcf1 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github description: API docs for the canvas plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'canvas'] --- import canvasObj from './canvas.devdocs.json'; diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx index e3c7e3cea6522..c6dd67e43d07e 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github description: API docs for the cases plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] --- import casesObj from './cases.devdocs.json'; diff --git a/api_docs/charts.devdocs.json b/api_docs/charts.devdocs.json index c2c62ac98565d..abf1df8307859 100644 --- a/api_docs/charts.devdocs.json +++ b/api_docs/charts.devdocs.json @@ -1626,28 +1626,6 @@ "deprecated": false, "trackAdoption": false, "initialIsOpen": false - }, - { - "parentPluginId": "charts", - "id": "def-public.truncatedColorSchemas", - "type": "Array", - "tags": [], - "label": "truncatedColorSchemas", - "description": [], - "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.ColorSchema", - "text": "ColorSchema" - }, - "[]" - ], - "path": "src/plugins/charts/common/static/color_maps/truncated_color_maps.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false } ], "objects": [ @@ -1834,19 +1812,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "charts", - "id": "def-public.truncatedColorMaps", - "type": "Object", - "tags": [], - "label": "truncatedColorMaps", - "description": [], - "path": "src/plugins/charts/common/static/color_maps/truncated_color_maps.ts", - "deprecated": false, - "trackAdoption": false, - "children": [], - "initialIsOpen": false - }, { "parentPluginId": "charts", "id": "def-public.vislibColorMaps", @@ -3378,6 +3343,21 @@ } ], "misc": [ + { + "parentPluginId": "charts", + "id": "def-common.AllowedChartOverrides", + "type": "Type", + "tags": [], + "label": "AllowedChartOverrides", + "description": [], + "signature": [ + "{ chart?: { description?: string | undefined; title?: string | undefined; } | undefined; }" + ], + "path": "src/plugins/charts/common/static/overrides/settings.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "charts", "id": "def-common.AllowedSettingsOverrides", @@ -3609,28 +3589,6 @@ "deprecated": false, "trackAdoption": false, "initialIsOpen": false - }, - { - "parentPluginId": "charts", - "id": "def-common.truncatedColorSchemas", - "type": "Array", - "tags": [], - "label": "truncatedColorSchemas", - "description": [], - "signature": [ - { - "pluginId": "charts", - "scope": "common", - "docId": "kibChartsPluginApi", - "section": "def-common.ColorSchema", - "text": "ColorSchema" - }, - "[]" - ], - "path": "src/plugins/charts/common/static/color_maps/truncated_color_maps.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false } ], "objects": [ @@ -3817,19 +3775,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "charts", - "id": "def-common.truncatedColorMaps", - "type": "Object", - "tags": [], - "label": "truncatedColorMaps", - "description": [], - "path": "src/plugins/charts/common/static/color_maps/truncated_color_maps.ts", - "deprecated": false, - "trackAdoption": false, - "children": [], - "initialIsOpen": false - }, { "parentPluginId": "charts", "id": "def-common.vislibColorMaps", diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index 3425efc509454..479c3cdeca646 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github description: API docs for the charts plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'charts'] --- import chartsObj from './charts.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 271 | 16 | 256 | 10 | +| 268 | 16 | 253 | 10 | ## Client diff --git a/api_docs/cloud.devdocs.json b/api_docs/cloud.devdocs.json index 3d9e7dd03898f..bf2875c387ff7 100644 --- a/api_docs/cloud.devdocs.json +++ b/api_docs/cloud.devdocs.json @@ -85,6 +85,20 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "cloud", + "id": "def-public.CloudConfigType.billing_url", + "type": "string", + "tags": [], + "label": "billing_url", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/cloud/public/plugin.tsx", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "cloud", "id": "def-public.CloudConfigType.organization_url", @@ -249,6 +263,22 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "cloud", + "id": "def-public.CloudStart.billingUrl", + "type": "string", + "tags": [], + "label": "billingUrl", + "description": [ + "\nThe full URL to the billing page on Elastic Cloud. Undefined if not running on Cloud." + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/cloud/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "cloud", "id": "def-public.CloudStart.organizationUrl", diff --git a/api_docs/cloud.mdx b/api_docs/cloud.mdx index eb31c684c9d3a..554372c5a7ed3 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github description: API docs for the cloud plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 52 | 0 | 11 | 0 | +| 54 | 0 | 12 | 0 | ## Client diff --git a/api_docs/cloud_chat.mdx b/api_docs/cloud_chat.mdx index da6d338953ea6..4de2551a154c6 100644 --- a/api_docs/cloud_chat.mdx +++ b/api_docs/cloud_chat.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudChat title: "cloudChat" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudChat plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudChat'] --- import cloudChatObj from './cloud_chat.devdocs.json'; diff --git a/api_docs/cloud_chat_provider.mdx b/api_docs/cloud_chat_provider.mdx index e89d26d4633d9..d1205180fca8e 100644 --- a/api_docs/cloud_chat_provider.mdx +++ b/api_docs/cloud_chat_provider.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudChatProvider title: "cloudChatProvider" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudChatProvider plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudChatProvider'] --- import cloudChatProviderObj from './cloud_chat_provider.devdocs.json'; diff --git a/api_docs/cloud_data_migration.mdx b/api_docs/cloud_data_migration.mdx index 85c3f9decab95..c2dc31f4d57a0 100644 --- a/api_docs/cloud_data_migration.mdx +++ b/api_docs/cloud_data_migration.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDataMigration title: "cloudDataMigration" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDataMigration plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDataMigration'] --- import cloudDataMigrationObj from './cloud_data_migration.devdocs.json'; diff --git a/api_docs/cloud_defend.mdx b/api_docs/cloud_defend.mdx index 79333feecc9bb..55ba690827802 100644 --- a/api_docs/cloud_defend.mdx +++ b/api_docs/cloud_defend.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDefend title: "cloudDefend" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDefend plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDefend'] --- import cloudDefendObj from './cloud_defend.devdocs.json'; diff --git a/api_docs/cloud_experiments.mdx b/api_docs/cloud_experiments.mdx index fe046ab8337ad..d9730ff4cf9e8 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudExperiments'] --- import cloudExperimentsObj from './cloud_experiments.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index a62a0e8209dff..19be173117b07 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudSecurityPosture plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudSecurityPosture'] --- import cloudSecurityPostureObj from './cloud_security_posture.devdocs.json'; diff --git a/api_docs/console.mdx b/api_docs/console.mdx index e04d9ceb461b5..176380ce83ac1 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github description: API docs for the console plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/content_management.mdx b/api_docs/content_management.mdx index cb6873c7ab74c..b22e9513e94db 100644 --- a/api_docs/content_management.mdx +++ b/api_docs/content_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/contentManagement title: "contentManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the contentManagement plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'contentManagement'] --- import contentManagementObj from './content_management.devdocs.json'; diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index af0579a61f942..2268e61817c72 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github description: API docs for the controls plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index 0af4c903bec62..299103af8c7ec 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github description: API docs for the customIntegrations plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'customIntegrations'] --- import customIntegrationsObj from './custom_integrations.devdocs.json'; diff --git a/api_docs/dashboard.mdx b/api_docs/dashboard.mdx index e360eded64ab8..16a417cacb7a5 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboard plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboard'] --- import dashboardObj from './dashboard.devdocs.json'; diff --git a/api_docs/dashboard_enhanced.mdx b/api_docs/dashboard_enhanced.mdx index c5ce53d1f8b66..9ff6f2330f88b 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboardEnhanced plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.mdx b/api_docs/data.mdx index 9ecf8580ab762..9551b7cebe961 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github description: API docs for the data plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index ce841f73084a6..9bb77627bfcaa 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github description: API docs for the data.query plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 5eddd6d09b2ac..0f93446b3f05a 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github description: API docs for the data.search plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 2159e38e38468..3e737d288086c 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewEditor plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewEditor'] --- import dataViewEditorObj from './data_view_editor.devdocs.json'; diff --git a/api_docs/data_view_field_editor.mdx b/api_docs/data_view_field_editor.mdx index d69b6df539cd2..1ae9fad417e5d 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewFieldEditor plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewFieldEditor'] --- import dataViewFieldEditorObj from './data_view_field_editor.devdocs.json'; diff --git a/api_docs/data_view_management.mdx b/api_docs/data_view_management.mdx index 154f8b8cf5da3..37f9b4946d837 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewManagement plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] --- import dataViewManagementObj from './data_view_management.devdocs.json'; diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index 44bd02caec702..fa611f10b8e36 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViews plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViews'] --- import dataViewsObj from './data_views.devdocs.json'; diff --git a/api_docs/data_visualizer.mdx b/api_docs/data_visualizer.mdx index b06578e5105c3..bda216ec62175 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github description: API docs for the dataVisualizer plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index aabc7eaaaa889..550f6f6816309 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index fc10a044869ca..db32045f12817 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index ed9272a818d87..6c7f4a1b499f3 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team description: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 28a1d98a08541..96ffcfe43d38d 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github description: API docs for the devTools plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] --- import devToolsObj from './dev_tools.devdocs.json'; diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index eaa7e9dc5f378..c1d76eb83346c 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github description: API docs for the discover plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discover'] --- import discoverObj from './discover.devdocs.json'; diff --git a/api_docs/discover_enhanced.mdx b/api_docs/discover_enhanced.mdx index df3a1d0c882f6..2b842e492a9b6 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverEnhanced plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/ecs_data_quality_dashboard.mdx b/api_docs/ecs_data_quality_dashboard.mdx index ba0c81fe3d532..04d584f7282f9 100644 --- a/api_docs/ecs_data_quality_dashboard.mdx +++ b/api_docs/ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ecsDataQualityDashboard title: "ecsDataQualityDashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the ecsDataQualityDashboard plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ecsDataQualityDashboard'] --- import ecsDataQualityDashboardObj from './ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index f1d93502a483e..9f214e29aca76 100644 --- a/api_docs/embeddable.mdx +++ b/api_docs/embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddable title: "embeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddable plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddable'] --- import embeddableObj from './embeddable.devdocs.json'; diff --git a/api_docs/embeddable_enhanced.mdx b/api_docs/embeddable_enhanced.mdx index 0e59e0fb9a7aa..c657b33310409 100644 --- a/api_docs/embeddable_enhanced.mdx +++ b/api_docs/embeddable_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced title: "embeddableEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddableEnhanced plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddableEnhanced'] --- import embeddableEnhancedObj from './embeddable_enhanced.devdocs.json'; diff --git a/api_docs/encrypted_saved_objects.mdx b/api_docs/encrypted_saved_objects.mdx index 337857d40fed0..87bec77293be6 100644 --- a/api_docs/encrypted_saved_objects.mdx +++ b/api_docs/encrypted_saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects title: "encryptedSavedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the encryptedSavedObjects plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'encryptedSavedObjects'] --- import encryptedSavedObjectsObj from './encrypted_saved_objects.devdocs.json'; diff --git a/api_docs/enterprise_search.mdx b/api_docs/enterprise_search.mdx index 07ccebc213360..0757a18e10a19 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the enterpriseSearch plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] --- import enterpriseSearchObj from './enterprise_search.devdocs.json'; diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index c04f0a59a21bb..8dcd203f43d13 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github description: API docs for the esUiShared plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/ess_security.mdx b/api_docs/ess_security.mdx index 399b025106548..856ed0b67c624 100644 --- a/api_docs/ess_security.mdx +++ b/api_docs/ess_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/essSecurity title: "essSecurity" image: https://source.unsplash.com/400x175/?github description: API docs for the essSecurity plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'essSecurity'] --- import essSecurityObj from './ess_security.devdocs.json'; diff --git a/api_docs/event_annotation.devdocs.json b/api_docs/event_annotation.devdocs.json index 32aa51ade4db3..3b205ea85a472 100644 --- a/api_docs/event_annotation.devdocs.json +++ b/api_docs/event_annotation.devdocs.json @@ -1752,7 +1752,7 @@ "label": "AvailableAnnotationIcon", "description": [], "signature": [ - "\"alert\" | \"circle\" | \"asterisk\" | \"bell\" | \"bolt\" | \"bug\" | \"editorComment\" | \"flag\" | \"heart\" | \"mapMarker\" | \"pinFilled\" | \"starEmpty\" | \"starFilled\" | \"tag\" | \"triangle\"" + "\"alert\" | \"circle\" | \"tag\" | \"asterisk\" | \"bell\" | \"bolt\" | \"bug\" | \"editorComment\" | \"flag\" | \"heart\" | \"mapMarker\" | \"pinFilled\" | \"starEmpty\" | \"starFilled\" | \"triangle\"" ], "path": "src/plugins/event_annotation/common/types.ts", "deprecated": false, @@ -2545,7 +2545,7 @@ "label": "options", "description": [], "signature": [ - "(\"alert\" | \"circle\" | \"asterisk\" | \"bell\" | \"bolt\" | \"bug\" | \"editorComment\" | \"flag\" | \"heart\" | \"mapMarker\" | \"pinFilled\" | \"starEmpty\" | \"starFilled\" | \"tag\" | \"triangle\")[]" + "(\"alert\" | \"circle\" | \"tag\" | \"asterisk\" | \"bell\" | \"bolt\" | \"bug\" | \"editorComment\" | \"flag\" | \"heart\" | \"mapMarker\" | \"pinFilled\" | \"starEmpty\" | \"starFilled\" | \"triangle\")[]" ], "path": "src/plugins/event_annotation/common/manual_event_annotation/index.ts", "deprecated": false, @@ -3712,7 +3712,7 @@ "label": "options", "description": [], "signature": [ - "(\"alert\" | \"circle\" | \"asterisk\" | \"bell\" | \"bolt\" | \"bug\" | \"editorComment\" | \"flag\" | \"heart\" | \"mapMarker\" | \"pinFilled\" | \"starEmpty\" | \"starFilled\" | \"tag\" | \"triangle\")[]" + "(\"alert\" | \"circle\" | \"tag\" | \"asterisk\" | \"bell\" | \"bolt\" | \"bug\" | \"editorComment\" | \"flag\" | \"heart\" | \"mapMarker\" | \"pinFilled\" | \"starEmpty\" | \"starFilled\" | \"triangle\")[]" ], "path": "src/plugins/event_annotation/common/query_point_event_annotation/index.ts", "deprecated": false, diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index f91cb517feb63..a6ba105b7a6a2 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotation plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index 3f92b5605985c..bfd0d8f64ebfe 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github description: API docs for the eventLog plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/exploratory_view.mdx b/api_docs/exploratory_view.mdx index f4500594093aa..0e550c333dc30 100644 --- a/api_docs/exploratory_view.mdx +++ b/api_docs/exploratory_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/exploratoryView title: "exploratoryView" image: https://source.unsplash.com/400x175/?github description: API docs for the exploratoryView plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'exploratoryView'] --- import exploratoryViewObj from './exploratory_view.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index e5740a7369505..62c5aed54bee2 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionError plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionError'] --- import expressionErrorObj from './expression_error.devdocs.json'; diff --git a/api_docs/expression_gauge.devdocs.json b/api_docs/expression_gauge.devdocs.json index b3767918876fb..eb53b79bbe044 100644 --- a/api_docs/expression_gauge.devdocs.json +++ b/api_docs/expression_gauge.devdocs.json @@ -1185,9 +1185,9 @@ "section": "def-common.FormatFactory", "text": "FormatFactory" }, - "; chartsThemeService: ", - "Theme", - "; paletteService: ", + "; chartsThemeService: Omit<", + "ThemeService", + ", \"init\">; paletteService: ", { "pluginId": "@kbn/coloring", "scope": "common", @@ -1265,7 +1265,7 @@ "CustomXDomain", " | undefined>; ariaDescription?: string | undefined; ariaDescribedBy?: string | undefined; ariaLabelledBy?: string | undefined; ariaTableCaption?: string | undefined; legendAction?: \"ignore\" | undefined; legendStrategy?: ", "LegendStrategy", - " | undefined; onLegendItemClick?: \"ignore\" | undefined; customLegend?: \"ignore\" | undefined; onLegendItemMinusClick?: \"ignore\" | undefined; onLegendItemOut?: \"ignore\" | undefined; onLegendItemOver?: \"ignore\" | undefined; onLegendItemPlusClick?: \"ignore\" | undefined; debugState?: boolean | undefined; onProjectionClick?: \"ignore\" | undefined; onElementClick?: \"ignore\" | undefined; onElementOver?: \"ignore\" | undefined; onElementOut?: \"ignore\" | undefined; onBrushEnd?: \"ignore\" | undefined; onProjectionAreaChange?: \"ignore\" | undefined; onAnnotationClick?: \"ignore\" | undefined; pointerUpdateDebounce?: number | undefined; roundHistogramBrushValues?: boolean | undefined; noResults?: React.ReactChild | React.ComponentType<{}> | undefined; legendSort?: \"ignore\" | undefined; }>>) | undefined; }" + " | undefined; onLegendItemClick?: \"ignore\" | undefined; customLegend?: \"ignore\" | undefined; onLegendItemMinusClick?: \"ignore\" | undefined; onLegendItemOut?: \"ignore\" | undefined; onLegendItemOver?: \"ignore\" | undefined; onLegendItemPlusClick?: \"ignore\" | undefined; debugState?: boolean | undefined; onProjectionClick?: \"ignore\" | undefined; onElementClick?: \"ignore\" | undefined; onElementOver?: \"ignore\" | undefined; onElementOut?: \"ignore\" | undefined; onBrushEnd?: \"ignore\" | undefined; onProjectionAreaChange?: \"ignore\" | undefined; onAnnotationClick?: \"ignore\" | undefined; pointerUpdateDebounce?: number | undefined; roundHistogramBrushValues?: boolean | undefined; noResults?: React.ReactChild | React.ComponentType<{}> | undefined; legendSort?: \"ignore\" | undefined; }>> & Partial>) | undefined; }" ], "path": "src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts", "deprecated": false, diff --git a/api_docs/expression_gauge.mdx b/api_docs/expression_gauge.mdx index 7def1b81a5a4f..25ca29169fd79 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionGauge plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionGauge'] --- import expressionGaugeObj from './expression_gauge.devdocs.json'; diff --git a/api_docs/expression_heatmap.devdocs.json b/api_docs/expression_heatmap.devdocs.json index 50e3f743d7330..91fc345d8f6c3 100644 --- a/api_docs/expression_heatmap.devdocs.json +++ b/api_docs/expression_heatmap.devdocs.json @@ -488,12 +488,12 @@ { "parentPluginId": "expressionHeatmap", "id": "def-common.HeatmapExpressionProps.overrides", - "type": "Object", + "type": "CompoundType", "tags": [], "label": "overrides", "description": [], "signature": [ - "Partial; ariaDescription?: string | undefined; ariaDescribedBy?: string | undefined; ariaLabelledBy?: string | undefined; ariaTableCaption?: string | undefined; legendAction?: \"ignore\" | undefined; legendStrategy?: ", "LegendStrategy", - " | undefined; onLegendItemClick?: \"ignore\" | undefined; customLegend?: \"ignore\" | undefined; onLegendItemMinusClick?: \"ignore\" | undefined; onLegendItemOut?: \"ignore\" | undefined; onLegendItemOver?: \"ignore\" | undefined; onLegendItemPlusClick?: \"ignore\" | undefined; debugState?: boolean | undefined; onProjectionClick?: \"ignore\" | undefined; onElementClick?: \"ignore\" | undefined; onElementOver?: \"ignore\" | undefined; onElementOut?: \"ignore\" | undefined; onBrushEnd?: \"ignore\" | undefined; onProjectionAreaChange?: \"ignore\" | undefined; onAnnotationClick?: \"ignore\" | undefined; pointerUpdateDebounce?: number | undefined; roundHistogramBrushValues?: boolean | undefined; noResults?: React.ReactChild | React.ComponentType<{}> | undefined; legendSort?: \"ignore\" | undefined; }>> | undefined" + " | undefined; onLegendItemClick?: \"ignore\" | undefined; customLegend?: \"ignore\" | undefined; onLegendItemMinusClick?: \"ignore\" | undefined; onLegendItemOut?: \"ignore\" | undefined; onLegendItemOver?: \"ignore\" | undefined; onLegendItemPlusClick?: \"ignore\" | undefined; debugState?: boolean | undefined; onProjectionClick?: \"ignore\" | undefined; onElementClick?: \"ignore\" | undefined; onElementOver?: \"ignore\" | undefined; onElementOut?: \"ignore\" | undefined; onBrushEnd?: \"ignore\" | undefined; onProjectionAreaChange?: \"ignore\" | undefined; onAnnotationClick?: \"ignore\" | undefined; pointerUpdateDebounce?: number | undefined; roundHistogramBrushValues?: boolean | undefined; noResults?: React.ReactChild | React.ComponentType<{}> | undefined; legendSort?: \"ignore\" | undefined; }>> & Partial>) | undefined" ], "path": "src/plugins/chart_expressions/expression_heatmap/common/types/expression_functions.ts", "deprecated": false, @@ -869,9 +869,9 @@ "section": "def-common.FormatFactory", "text": "FormatFactory" }, - "; chartsThemeService: ", - "Theme", - "; chartsActiveCursorService: ", + "; chartsThemeService: Omit<", + "ThemeService", + ", \"init\">; chartsActiveCursorService: ", "ActiveCursor", "; datatableUtilities: ", { diff --git a/api_docs/expression_heatmap.mdx b/api_docs/expression_heatmap.mdx index bbcc3e7b554f2..aa3ecef0635b6 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionHeatmap plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionHeatmap'] --- import expressionHeatmapObj from './expression_heatmap.devdocs.json'; diff --git a/api_docs/expression_image.mdx b/api_docs/expression_image.mdx index 67dddb60149ec..cb9cf1e79b81e 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionImage plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionImage'] --- import expressionImageObj from './expression_image.devdocs.json'; diff --git a/api_docs/expression_legacy_metric_vis.mdx b/api_docs/expression_legacy_metric_vis.mdx index 83a31df66a811..bd845b069beef 100644 --- a/api_docs/expression_legacy_metric_vis.mdx +++ b/api_docs/expression_legacy_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionLegacyMetricVis title: "expressionLegacyMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionLegacyMetricVis plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionLegacyMetricVis'] --- import expressionLegacyMetricVisObj from './expression_legacy_metric_vis.devdocs.json'; diff --git a/api_docs/expression_metric.mdx b/api_docs/expression_metric.mdx index 58dc2b1d88475..4790a1440a31c 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetric plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetric'] --- import expressionMetricObj from './expression_metric.devdocs.json'; diff --git a/api_docs/expression_metric_vis.devdocs.json b/api_docs/expression_metric_vis.devdocs.json index 22d54b78b9e6d..dfd0f3a000d80 100644 --- a/api_docs/expression_metric_vis.devdocs.json +++ b/api_docs/expression_metric_vis.devdocs.json @@ -863,12 +863,12 @@ { "parentPluginId": "expressionMetricVis", "id": "def-common.MetricVisRenderConfig.overrides", - "type": "Object", + "type": "CompoundType", "tags": [], "label": "overrides", "description": [], "signature": [ - "Partial; ariaDescription?: string | undefined; ariaDescribedBy?: string | undefined; ariaLabelledBy?: string | undefined; ariaTableCaption?: string | undefined; legendAction?: \"ignore\" | undefined; legendStrategy?: ", "LegendStrategy", - " | undefined; onLegendItemClick?: \"ignore\" | undefined; customLegend?: \"ignore\" | undefined; onLegendItemMinusClick?: \"ignore\" | undefined; onLegendItemOut?: \"ignore\" | undefined; onLegendItemOver?: \"ignore\" | undefined; onLegendItemPlusClick?: \"ignore\" | undefined; debugState?: boolean | undefined; onProjectionClick?: \"ignore\" | undefined; onElementClick?: \"ignore\" | undefined; onElementOver?: \"ignore\" | undefined; onElementOut?: \"ignore\" | undefined; onBrushEnd?: \"ignore\" | undefined; onProjectionAreaChange?: \"ignore\" | undefined; onAnnotationClick?: \"ignore\" | undefined; pointerUpdateDebounce?: number | undefined; roundHistogramBrushValues?: boolean | undefined; noResults?: React.ReactChild | React.ComponentType<{}> | undefined; legendSort?: \"ignore\" | undefined; }>> | undefined" + " | undefined; onLegendItemClick?: \"ignore\" | undefined; customLegend?: \"ignore\" | undefined; onLegendItemMinusClick?: \"ignore\" | undefined; onLegendItemOut?: \"ignore\" | undefined; onLegendItemOver?: \"ignore\" | undefined; onLegendItemPlusClick?: \"ignore\" | undefined; debugState?: boolean | undefined; onProjectionClick?: \"ignore\" | undefined; onElementClick?: \"ignore\" | undefined; onElementOver?: \"ignore\" | undefined; onElementOut?: \"ignore\" | undefined; onBrushEnd?: \"ignore\" | undefined; onProjectionAreaChange?: \"ignore\" | undefined; onAnnotationClick?: \"ignore\" | undefined; pointerUpdateDebounce?: number | undefined; roundHistogramBrushValues?: boolean | undefined; noResults?: React.ReactChild | React.ComponentType<{}> | undefined; legendSort?: \"ignore\" | undefined; }>> & Partial>) | undefined" ], "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", "deprecated": false, @@ -1012,7 +1012,7 @@ "label": "AvailableMetricIcon", "description": [], "signature": [ - "\"alert\" | \"temperature\" | \"asterisk\" | \"bell\" | \"bolt\" | \"bug\" | \"compute\" | \"editorComment\" | \"empty\" | \"flag\" | \"globe\" | \"heart\" | \"mapMarker\" | \"pin\" | \"sortDown\" | \"sortUp\" | \"starEmpty\" | \"tag\"" + "\"alert\" | \"tag\" | \"temperature\" | \"asterisk\" | \"bell\" | \"bolt\" | \"bug\" | \"compute\" | \"editorComment\" | \"empty\" | \"flag\" | \"globe\" | \"heart\" | \"mapMarker\" | \"pin\" | \"sortDown\" | \"sortUp\" | \"starEmpty\"" ], "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", "deprecated": false, diff --git a/api_docs/expression_metric_vis.mdx b/api_docs/expression_metric_vis.mdx index 0c4232609f3f0..a5fda431f88e6 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetricVis plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetricVis'] --- import expressionMetricVisObj from './expression_metric_vis.devdocs.json'; diff --git a/api_docs/expression_partition_vis.mdx b/api_docs/expression_partition_vis.mdx index 813bc296286a2..6dc2d107f845c 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionPartitionVis plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionPartitionVis'] --- import expressionPartitionVisObj from './expression_partition_vis.devdocs.json'; diff --git a/api_docs/expression_repeat_image.mdx b/api_docs/expression_repeat_image.mdx index f20a7524b60b5..c4b99bdb1cd89 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRepeatImage plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRepeatImage'] --- import expressionRepeatImageObj from './expression_repeat_image.devdocs.json'; diff --git a/api_docs/expression_reveal_image.mdx b/api_docs/expression_reveal_image.mdx index f9b6ba47e7433..b4e92499d5a59 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRevealImage plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRevealImage'] --- import expressionRevealImageObj from './expression_reveal_image.devdocs.json'; diff --git a/api_docs/expression_shape.mdx b/api_docs/expression_shape.mdx index ef603320e6d0f..a8212bf874358 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionShape plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionShape'] --- import expressionShapeObj from './expression_shape.devdocs.json'; diff --git a/api_docs/expression_tagcloud.mdx b/api_docs/expression_tagcloud.mdx index 2ba509c870682..05d7b916e4d52 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionTagcloud plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.devdocs.json b/api_docs/expression_x_y.devdocs.json index 48f2615fc4e12..dea5fb9e26e9d 100644 --- a/api_docs/expression_x_y.devdocs.json +++ b/api_docs/expression_x_y.devdocs.json @@ -1950,7 +1950,7 @@ "CustomXDomain", " | undefined>; ariaDescription?: string | undefined; ariaDescribedBy?: string | undefined; ariaLabelledBy?: string | undefined; ariaTableCaption?: string | undefined; legendAction?: \"ignore\" | undefined; legendStrategy?: ", "LegendStrategy", - " | undefined; onLegendItemClick?: \"ignore\" | undefined; customLegend?: \"ignore\" | undefined; onLegendItemMinusClick?: \"ignore\" | undefined; onLegendItemOut?: \"ignore\" | undefined; onLegendItemOver?: \"ignore\" | undefined; onLegendItemPlusClick?: \"ignore\" | undefined; debugState?: boolean | undefined; onProjectionClick?: \"ignore\" | undefined; onElementClick?: \"ignore\" | undefined; onElementOver?: \"ignore\" | undefined; onElementOut?: \"ignore\" | undefined; onBrushEnd?: \"ignore\" | undefined; onProjectionAreaChange?: \"ignore\" | undefined; onAnnotationClick?: \"ignore\" | undefined; pointerUpdateDebounce?: number | undefined; roundHistogramBrushValues?: boolean | undefined; noResults?: React.ReactChild | React.ComponentType<{}> | undefined; legendSort?: \"ignore\" | undefined; }>>) | undefined" + " | undefined; onLegendItemClick?: \"ignore\" | undefined; customLegend?: \"ignore\" | undefined; onLegendItemMinusClick?: \"ignore\" | undefined; onLegendItemOut?: \"ignore\" | undefined; onLegendItemOver?: \"ignore\" | undefined; onLegendItemPlusClick?: \"ignore\" | undefined; debugState?: boolean | undefined; onProjectionClick?: \"ignore\" | undefined; onElementClick?: \"ignore\" | undefined; onElementOver?: \"ignore\" | undefined; onElementOut?: \"ignore\" | undefined; onBrushEnd?: \"ignore\" | undefined; onProjectionAreaChange?: \"ignore\" | undefined; onAnnotationClick?: \"ignore\" | undefined; pointerUpdateDebounce?: number | undefined; roundHistogramBrushValues?: boolean | undefined; noResults?: React.ReactChild | React.ComponentType<{}> | undefined; legendSort?: \"ignore\" | undefined; }>> & Partial>) | undefined" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts", "deprecated": false, @@ -2236,7 +2236,7 @@ "label": "AvailableReferenceLineIcon", "description": [], "signature": [ - "\"alert\" | \"circle\" | \"asterisk\" | \"bell\" | \"bolt\" | \"bug\" | \"editorComment\" | \"empty\" | \"flag\" | \"heart\" | \"mapMarker\" | \"pinFilled\" | \"starEmpty\" | \"starFilled\" | \"tag\" | \"triangle\"" + "\"alert\" | \"circle\" | \"tag\" | \"asterisk\" | \"bell\" | \"bolt\" | \"bug\" | \"editorComment\" | \"empty\" | \"flag\" | \"heart\" | \"mapMarker\" | \"pinFilled\" | \"starEmpty\" | \"starFilled\" | \"triangle\"" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", "deprecated": false, diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index 3b89a347dbf57..2f41edf8ec728 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionXY plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index fc95731a8a0b6..4f29157f0d332 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github description: API docs for the expressions plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressions'] --- import expressionsObj from './expressions.devdocs.json'; diff --git a/api_docs/features.mdx b/api_docs/features.mdx index 127c2d2822f6f..f6d4c24f36bb9 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github description: API docs for the features plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'features'] --- import featuresObj from './features.devdocs.json'; diff --git a/api_docs/field_formats.mdx b/api_docs/field_formats.mdx index 687978bccc8ad..2838e587f6f9f 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldFormats plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] --- import fieldFormatsObj from './field_formats.devdocs.json'; diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index ad991578c2ab9..dba63ce83efd0 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github description: API docs for the fileUpload plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] --- import fileUploadObj from './file_upload.devdocs.json'; diff --git a/api_docs/files.devdocs.json b/api_docs/files.devdocs.json index 6b2aba6f8174f..40f738a455dc1 100644 --- a/api_docs/files.devdocs.json +++ b/api_docs/files.devdocs.json @@ -2058,7 +2058,7 @@ "tags": [], "label": "indexIsAlias", "description": [ - "\nTreat the indices provided as Aliases. If set to true, ES `search()` will be used to\nretrieve the file info and content instead of `get()`. This is needed to ensure the\ncontent can be retrieved in cases where an index may have rolled over (ES `get()`\nneeds a \"real\" index)" + "\nTreat the indices provided as Aliases/Datastreams.\nWhen set to `true`:\n- additional ES calls will be made to get the real backing indexes\n- will not check if indexes exists and attempt to create them if not\n- an additional `@timestamp` property will be written to all documents (at root of document)" ], "signature": [ "boolean | undefined" diff --git a/api_docs/files.mdx b/api_docs/files.mdx index 4e809c08a8c47..6db8d183a55ce 100644 --- a/api_docs/files.mdx +++ b/api_docs/files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/files title: "files" image: https://source.unsplash.com/400x175/?github description: API docs for the files plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; diff --git a/api_docs/files_management.mdx b/api_docs/files_management.mdx index 8a3d40d0b9191..cc9fa3ccf9895 100644 --- a/api_docs/files_management.mdx +++ b/api_docs/files_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/filesManagement title: "filesManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the filesManagement plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'filesManagement'] --- import filesManagementObj from './files_management.devdocs.json'; diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index 4cb5430104b85..e3561ff67f72f 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the fleet plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index e5ea2aeecd2a7..7fecdc4a0cf48 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the globalSearch plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] --- import globalSearchObj from './global_search.devdocs.json'; diff --git a/api_docs/guided_onboarding.mdx b/api_docs/guided_onboarding.mdx index e53b84ea30c45..693fb5e1bc6a2 100644 --- a/api_docs/guided_onboarding.mdx +++ b/api_docs/guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/guidedOnboarding title: "guidedOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the guidedOnboarding plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'guidedOnboarding'] --- import guidedOnboardingObj from './guided_onboarding.devdocs.json'; diff --git a/api_docs/home.mdx b/api_docs/home.mdx index c006f13e44f54..066cbf18f7bfe 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github description: API docs for the home plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] --- import homeObj from './home.devdocs.json'; diff --git a/api_docs/image_embeddable.mdx b/api_docs/image_embeddable.mdx index 37874756ae600..292d0a0ee2565 100644 --- a/api_docs/image_embeddable.mdx +++ b/api_docs/image_embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/imageEmbeddable title: "imageEmbeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the imageEmbeddable plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'imageEmbeddable'] --- import imageEmbeddableObj from './image_embeddable.devdocs.json'; diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index 5e7e7abc13378..489440e7892ee 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexLifecycleManagement plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexLifecycleManagement'] --- import indexLifecycleManagementObj from './index_lifecycle_management.devdocs.json'; diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx index 9ba60a392c0cb..13174c35a6298 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexManagement plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index a95d95b2f1ae8..520d1eb1b99cf 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github description: API docs for the infra plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index 46015cb7fdf45..c669489aceb7b 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github description: API docs for the inspector plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index d95fbef2f92ed..ace377408281a 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github description: API docs for the interactiveSetup plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx index 97e504fba24a2..3510ccb3026ed 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index ec58624389c64..d8b747beb9973 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-components plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx index 284ab00535c41..073c7ad634370 100644 --- a/api_docs/kbn_aiops_utils.mdx +++ b/api_docs/kbn_aiops_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils title: "@kbn/aiops-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] --- import kbnAiopsUtilsObj from './kbn_aiops_utils.devdocs.json'; diff --git a/api_docs/kbn_alerting_state_types.mdx b/api_docs/kbn_alerting_state_types.mdx index 726cb14e740ff..3b07e03c7e8f7 100644 --- a/api_docs/kbn_alerting_state_types.mdx +++ b/api_docs/kbn_alerting_state_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-state-types title: "@kbn/alerting-state-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-state-types plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-state-types'] --- import kbnAlertingStateTypesObj from './kbn_alerting_state_types.devdocs.json'; diff --git a/api_docs/kbn_alerts_as_data_utils.mdx b/api_docs/kbn_alerts_as_data_utils.mdx index 95ccd9d032110..a89fadba6b905 100644 --- a/api_docs/kbn_alerts_as_data_utils.mdx +++ b/api_docs/kbn_alerts_as_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-as-data-utils title: "@kbn/alerts-as-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-as-data-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-as-data-utils'] --- import kbnAlertsAsDataUtilsObj from './kbn_alerts_as_data_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts_ui_shared.mdx b/api_docs/kbn_alerts_ui_shared.mdx index a7589efb2149e..f8116af96a29c 100644 --- a/api_docs/kbn_alerts_ui_shared.mdx +++ b/api_docs/kbn_alerts_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-ui-shared title: "@kbn/alerts-ui-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-ui-shared plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-ui-shared'] --- import kbnAlertsUiSharedObj from './kbn_alerts_ui_shared.devdocs.json'; diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index e9892d94dfaa1..6549daceac9df 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index 287ebd767d02b..487971dc0824d 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index f2ace21d05cb0..b84ca2a9ae3d6 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-browser'] --- import kbnAnalyticsShippersElasticV3BrowserObj from './kbn_analytics_shippers_elastic_v3_browser.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx index 03c8672a9ef25..d835ce46020aa 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-common'] --- import kbnAnalyticsShippersElasticV3CommonObj from './kbn_analytics_shippers_elastic_v3_common.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx index bbc79116ad84d..d8ef06e92a2d4 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-server'] --- import kbnAnalyticsShippersElasticV3ServerObj from './kbn_analytics_shippers_elastic_v3_server.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_fullstory.mdx b/api_docs/kbn_analytics_shippers_fullstory.mdx index 8121eecbf5b14..34aae13e5f909 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_gainsight.mdx b/api_docs/kbn_analytics_shippers_gainsight.mdx index 30fd472fb29e7..5903db6c22eb4 100644 --- a/api_docs/kbn_analytics_shippers_gainsight.mdx +++ b/api_docs/kbn_analytics_shippers_gainsight.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-gainsight title: "@kbn/analytics-shippers-gainsight" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-gainsight plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-gainsight'] --- import kbnAnalyticsShippersGainsightObj from './kbn_analytics_shippers_gainsight.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index dfd07d3e9a68d..ff477d2694ad9 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-config-loader plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] --- import kbnApmConfigLoaderObj from './kbn_apm_config_loader.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index 6fcab3e9754fc..d7c82e20ac204 100644 --- a/api_docs/kbn_apm_synthtrace.mdx +++ b/api_docs/kbn_apm_synthtrace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace title: "@kbn/apm-synthtrace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace_client.mdx b/api_docs/kbn_apm_synthtrace_client.mdx index 4e7c5470ccfc6..ddecff7dcdc3a 100644 --- a/api_docs/kbn_apm_synthtrace_client.mdx +++ b/api_docs/kbn_apm_synthtrace_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace-client title: "@kbn/apm-synthtrace-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace-client plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace-client'] --- import kbnApmSynthtraceClientObj from './kbn_apm_synthtrace_client.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index c8d7ad4403638..dfe6f14649adf 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index cf766ee6c3512..7cf209d3a67e8 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/axe-config plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_cases_components.mdx b/api_docs/kbn_cases_components.mdx index 4b8dc25f45ac9..929302a536fb9 100644 --- a/api_docs/kbn_cases_components.mdx +++ b/api_docs/kbn_cases_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cases-components title: "@kbn/cases-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cases-components plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cases-components'] --- import kbnCasesComponentsObj from './kbn_cases_components.devdocs.json'; diff --git a/api_docs/kbn_cell_actions.devdocs.json b/api_docs/kbn_cell_actions.devdocs.json index a25ef677ee34e..bb7d9ec6611ff 100644 --- a/api_docs/kbn_cell_actions.devdocs.json +++ b/api_docs/kbn_cell_actions.devdocs.json @@ -36,13 +36,7 @@ "text": "FilterManager" }, " | undefined; fieldName: string; value: ", - { - "pluginId": "@kbn/cell-actions", - "scope": "common", - "docId": "kibKbnCellActionsPluginApi", - "section": "def-common.CellActionFieldValue", - "text": "CellActionFieldValue" - }, + "DefaultActionsSupportedValue", "; }) => void" ], "path": "packages/kbn-cell-actions/src/actions/filter/filter_in.ts", @@ -100,7 +94,7 @@ "label": "value", "description": [], "signature": [ - "string | number | boolean | string[] | boolean[] | number[] | null | undefined" + "string[] | boolean[] | number[]" ], "path": "packages/kbn-cell-actions/src/actions/filter/filter_in.ts", "deprecated": false, @@ -129,13 +123,7 @@ "text": "FilterManager" }, " | undefined; fieldName: string; value: ", - { - "pluginId": "@kbn/cell-actions", - "scope": "common", - "docId": "kibKbnCellActionsPluginApi", - "section": "def-common.CellActionFieldValue", - "text": "CellActionFieldValue" - }, + "DefaultActionsSupportedValue", "; }) => void" ], "path": "packages/kbn-cell-actions/src/actions/filter/filter_out.ts", @@ -193,7 +181,7 @@ "label": "value", "description": [], "signature": [ - "string | number | boolean | string[] | boolean[] | number[] | null | undefined" + "string[] | boolean[] | number[]" ], "path": "packages/kbn-cell-actions/src/actions/filter/filter_out.ts", "deprecated": false, @@ -460,6 +448,14 @@ "section": "def-public.FilterManager", "text": "FilterManager" }, + "; notifications: ", + { + "pluginId": "@kbn/core-notifications-browser", + "scope": "common", + "docId": "kibKbnCoreNotificationsBrowserPluginApi", + "section": "def-common.NotificationsStart", + "text": "NotificationsStart" + }, "; }) => ", { "pluginId": "@kbn/cell-actions", @@ -524,6 +520,14 @@ "section": "def-public.FilterManager", "text": "FilterManager" }, + "; notifications: ", + { + "pluginId": "@kbn/core-notifications-browser", + "scope": "common", + "docId": "kibKbnCoreNotificationsBrowserPluginApi", + "section": "def-common.NotificationsStart", + "text": "NotificationsStart" + }, "; }) => ", { "pluginId": "@kbn/cell-actions", @@ -1273,7 +1277,15 @@ "label": "CellActionFieldValue", "description": [], "signature": [ - "string | number | boolean | string[] | boolean[] | number[] | null | undefined" + "string[] | undefined[] | boolean[] | ", + { + "pluginId": "@kbn/utility-types", + "scope": "common", + "docId": "kibKbnUtilityTypesPluginApi", + "section": "def-common.Serializable", + "text": "Serializable" + }, + " | number[] | null[]" ], "path": "packages/kbn-cell-actions/src/types.ts", "deprecated": false, diff --git a/api_docs/kbn_cell_actions.mdx b/api_docs/kbn_cell_actions.mdx index b33eb81b6a1a2..5d822ead90c1b 100644 --- a/api_docs/kbn_cell_actions.mdx +++ b/api_docs/kbn_cell_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cell-actions title: "@kbn/cell-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cell-actions plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cell-actions'] --- import kbnCellActionsObj from './kbn_cell_actions.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-threat-hunting-explore](https://github.com/orgs/elast | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 62 | 1 | 44 | 2 | +| 62 | 1 | 44 | 3 | ## Common diff --git a/api_docs/kbn_chart_expressions_common.mdx b/api_docs/kbn_chart_expressions_common.mdx index cd02912cf61cc..2634b8ff3bd29 100644 --- a/api_docs/kbn_chart_expressions_common.mdx +++ b/api_docs/kbn_chart_expressions_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-expressions-common title: "@kbn/chart-expressions-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-expressions-common plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-expressions-common'] --- import kbnChartExpressionsCommonObj from './kbn_chart_expressions_common.devdocs.json'; diff --git a/api_docs/kbn_chart_icons.mdx b/api_docs/kbn_chart_icons.mdx index 5a84b25e188dd..8ede10441d779 100644 --- a/api_docs/kbn_chart_icons.mdx +++ b/api_docs/kbn_chart_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-icons title: "@kbn/chart-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-icons plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-icons'] --- import kbnChartIconsObj from './kbn_chart_icons.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_core.mdx b/api_docs/kbn_ci_stats_core.mdx index 9baba9fced13b..52f8c815893b1 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-core plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-core'] --- import kbnCiStatsCoreObj from './kbn_ci_stats_core.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_performance_metrics.mdx b/api_docs/kbn_ci_stats_performance_metrics.mdx index 48b91ddeb3ff0..b4c0a5bc34885 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-performance-metrics'] --- import kbnCiStatsPerformanceMetricsObj from './kbn_ci_stats_performance_metrics.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_reporter.mdx b/api_docs/kbn_ci_stats_reporter.mdx index 9c33e19d0ac89..086ac5b45494a 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-reporter plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-reporter'] --- import kbnCiStatsReporterObj from './kbn_ci_stats_reporter.devdocs.json'; diff --git a/api_docs/kbn_cli_dev_mode.mdx b/api_docs/kbn_cli_dev_mode.mdx index 2fc809b21c27d..414873ca5e0c2 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cli-dev-mode plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_code_editor.mdx b/api_docs/kbn_code_editor.mdx index a816cd623cbd0..ed494fbbffdbe 100644 --- a/api_docs/kbn_code_editor.mdx +++ b/api_docs/kbn_code_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor title: "@kbn/code-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] --- import kbnCodeEditorObj from './kbn_code_editor.devdocs.json'; diff --git a/api_docs/kbn_code_editor_mocks.mdx b/api_docs/kbn_code_editor_mocks.mdx index 67573c599cc48..a8fcbb433416b 100644 --- a/api_docs/kbn_code_editor_mocks.mdx +++ b/api_docs/kbn_code_editor_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor-mocks title: "@kbn/code-editor-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor-mocks'] --- import kbnCodeEditorMocksObj from './kbn_code_editor_mocks.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index 25e3b0e8d28c6..8a8436cfca4f5 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/coloring plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/coloring'] --- import kbnColoringObj from './kbn_coloring.devdocs.json'; diff --git a/api_docs/kbn_config.mdx b/api_docs/kbn_config.mdx index b0b76d58ad9d5..fb82faf4a7a27 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config'] --- import kbnConfigObj from './kbn_config.devdocs.json'; diff --git a/api_docs/kbn_config_mocks.mdx b/api_docs/kbn_config_mocks.mdx index 914fd72c83af5..3455e1e02089c 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-mocks'] --- import kbnConfigMocksObj from './kbn_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_config_schema.mdx b/api_docs/kbn_config_schema.mdx index e1f235e1d2127..c62ef76a639b5 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-schema plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_content_management_content_editor.mdx b/api_docs/kbn_content_management_content_editor.mdx index 83b44191d3e76..e31d1f88a6d91 100644 --- a/api_docs/kbn_content_management_content_editor.mdx +++ b/api_docs/kbn_content_management_content_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-editor title: "@kbn/content-management-content-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-editor plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-editor'] --- import kbnContentManagementContentEditorObj from './kbn_content_management_content_editor.devdocs.json'; diff --git a/api_docs/kbn_content_management_tabbed_table_list_view.mdx b/api_docs/kbn_content_management_tabbed_table_list_view.mdx index 5b4aa6403c757..22d2d4f11a703 100644 --- a/api_docs/kbn_content_management_tabbed_table_list_view.mdx +++ b/api_docs/kbn_content_management_tabbed_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-tabbed-table-list-view title: "@kbn/content-management-tabbed-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-tabbed-table-list-view plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-tabbed-table-list-view'] --- import kbnContentManagementTabbedTableListViewObj from './kbn_content_management_tabbed_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view.mdx b/api_docs/kbn_content_management_table_list_view.mdx index 4a643f4abd01b..385c8b7b37062 100644 --- a/api_docs/kbn_content_management_table_list_view.mdx +++ b/api_docs/kbn_content_management_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view title: "@kbn/content-management-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view'] --- import kbnContentManagementTableListViewObj from './kbn_content_management_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view_table.mdx b/api_docs/kbn_content_management_table_list_view_table.mdx index 36fc8b77b8819..feded3fb5194a 100644 --- a/api_docs/kbn_content_management_table_list_view_table.mdx +++ b/api_docs/kbn_content_management_table_list_view_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-table title: "@kbn/content-management-table-list-view-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-table plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view-table'] --- import kbnContentManagementTableListViewTableObj from './kbn_content_management_table_list_view_table.devdocs.json'; diff --git a/api_docs/kbn_content_management_utils.mdx b/api_docs/kbn_content_management_utils.mdx index 81f91c52398ce..db392a7e558a2 100644 --- a/api_docs/kbn_content_management_utils.mdx +++ b/api_docs/kbn_content_management_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-utils title: "@kbn/content-management-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-utils'] --- import kbnContentManagementUtilsObj from './kbn_content_management_utils.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index d9cbd1dcfeb84..f04fb919cc799 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser'] --- import kbnCoreAnalyticsBrowserObj from './kbn_core_analytics_browser.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_internal.mdx b/api_docs/kbn_core_analytics_browser_internal.mdx index 6975139723094..9b4fc1de7b358 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-internal'] --- import kbnCoreAnalyticsBrowserInternalObj from './kbn_core_analytics_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_mocks.mdx b/api_docs/kbn_core_analytics_browser_mocks.mdx index 57b0b1005246f..9b46410b4c5d5 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-mocks'] --- import kbnCoreAnalyticsBrowserMocksObj from './kbn_core_analytics_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server.mdx b/api_docs/kbn_core_analytics_server.mdx index 6e95e325b8513..f6024026e107b 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server'] --- import kbnCoreAnalyticsServerObj from './kbn_core_analytics_server.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_internal.mdx b/api_docs/kbn_core_analytics_server_internal.mdx index fea858147789d..ea4cd542ba79f 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-internal'] --- import kbnCoreAnalyticsServerInternalObj from './kbn_core_analytics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_mocks.mdx b/api_docs/kbn_core_analytics_server_mocks.mdx index 4926468326f17..7b7474182accc 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-mocks'] --- import kbnCoreAnalyticsServerMocksObj from './kbn_core_analytics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser.mdx b/api_docs/kbn_core_application_browser.mdx index 5eec364c82a91..45eafb44bfc20 100644 --- a/api_docs/kbn_core_application_browser.mdx +++ b/api_docs/kbn_core_application_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser title: "@kbn/core-application-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser'] --- import kbnCoreApplicationBrowserObj from './kbn_core_application_browser.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_internal.mdx b/api_docs/kbn_core_application_browser_internal.mdx index a42591a619af6..2ef325b1161b0 100644 --- a/api_docs/kbn_core_application_browser_internal.mdx +++ b/api_docs/kbn_core_application_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-internal title: "@kbn/core-application-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-internal'] --- import kbnCoreApplicationBrowserInternalObj from './kbn_core_application_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_mocks.mdx b/api_docs/kbn_core_application_browser_mocks.mdx index a7f3dc9896fa8..dc7c24e327b29 100644 --- a/api_docs/kbn_core_application_browser_mocks.mdx +++ b/api_docs/kbn_core_application_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-mocks title: "@kbn/core-application-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-mocks'] --- import kbnCoreApplicationBrowserMocksObj from './kbn_core_application_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_common.mdx b/api_docs/kbn_core_application_common.mdx index c787874beccfa..8a4c00d3d5b3d 100644 --- a/api_docs/kbn_core_application_common.mdx +++ b/api_docs/kbn_core_application_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-common title: "@kbn/core-application-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-common plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-common'] --- import kbnCoreApplicationCommonObj from './kbn_core_application_common.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_internal.mdx b/api_docs/kbn_core_apps_browser_internal.mdx index 4c4bd50d63557..db198460df556 100644 --- a/api_docs/kbn_core_apps_browser_internal.mdx +++ b/api_docs/kbn_core_apps_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-internal title: "@kbn/core-apps-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-internal'] --- import kbnCoreAppsBrowserInternalObj from './kbn_core_apps_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_mocks.mdx b/api_docs/kbn_core_apps_browser_mocks.mdx index c9ce8b9ef4ffb..d3179020d8a57 100644 --- a/api_docs/kbn_core_apps_browser_mocks.mdx +++ b/api_docs/kbn_core_apps_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-mocks title: "@kbn/core-apps-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-mocks'] --- import kbnCoreAppsBrowserMocksObj from './kbn_core_apps_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_apps_server_internal.mdx b/api_docs/kbn_core_apps_server_internal.mdx index 04fb2016aac80..c57aa5a6b905f 100644 --- a/api_docs/kbn_core_apps_server_internal.mdx +++ b/api_docs/kbn_core_apps_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-server-internal title: "@kbn/core-apps-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-server-internal'] --- import kbnCoreAppsServerInternalObj from './kbn_core_apps_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index 2a8cdfb9e35aa..5a5bafaad2647 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-browser-mocks'] --- import kbnCoreBaseBrowserMocksObj from './kbn_core_base_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_base_common.mdx b/api_docs/kbn_core_base_common.mdx index 3c2e12d30eb99..f35498753f0dc 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-common plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-common'] --- import kbnCoreBaseCommonObj from './kbn_core_base_common.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_internal.mdx b/api_docs/kbn_core_base_server_internal.mdx index 94bf124c8443d..20ba41af78b05 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-internal'] --- import kbnCoreBaseServerInternalObj from './kbn_core_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_mocks.mdx b/api_docs/kbn_core_base_server_mocks.mdx index b5b2de345636f..72e523cd3bfcc 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-mocks'] --- import kbnCoreBaseServerMocksObj from './kbn_core_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_browser_mocks.mdx b/api_docs/kbn_core_capabilities_browser_mocks.mdx index c0162cc0508d9..8bc58dcf9a0b1 100644 --- a/api_docs/kbn_core_capabilities_browser_mocks.mdx +++ b/api_docs/kbn_core_capabilities_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-browser-mocks title: "@kbn/core-capabilities-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-browser-mocks'] --- import kbnCoreCapabilitiesBrowserMocksObj from './kbn_core_capabilities_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_common.mdx b/api_docs/kbn_core_capabilities_common.mdx index b6c64bd693f11..49770b82a2317 100644 --- a/api_docs/kbn_core_capabilities_common.mdx +++ b/api_docs/kbn_core_capabilities_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-common title: "@kbn/core-capabilities-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-common plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-common'] --- import kbnCoreCapabilitiesCommonObj from './kbn_core_capabilities_common.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server.mdx b/api_docs/kbn_core_capabilities_server.mdx index d7719d9c61f05..80a41f737d7a8 100644 --- a/api_docs/kbn_core_capabilities_server.mdx +++ b/api_docs/kbn_core_capabilities_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server title: "@kbn/core-capabilities-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server'] --- import kbnCoreCapabilitiesServerObj from './kbn_core_capabilities_server.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server_mocks.mdx b/api_docs/kbn_core_capabilities_server_mocks.mdx index 3e36e267542d7..43feccb912076 100644 --- a/api_docs/kbn_core_capabilities_server_mocks.mdx +++ b/api_docs/kbn_core_capabilities_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server-mocks title: "@kbn/core-capabilities-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server-mocks'] --- import kbnCoreCapabilitiesServerMocksObj from './kbn_core_capabilities_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser.devdocs.json b/api_docs/kbn_core_chrome_browser.devdocs.json index 32156759fca8b..5d48630ad5476 100644 --- a/api_docs/kbn_core_chrome_browser.devdocs.json +++ b/api_docs/kbn_core_chrome_browser.devdocs.json @@ -671,6 +671,56 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-common.ChromeHelpMenuLink", + "type": "Interface", + "tags": [], + "label": "ChromeHelpMenuLink", + "description": [], + "path": "packages/core/chrome/core-chrome-browser/src/nav_controls.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-common.ChromeHelpMenuLink.title", + "type": "string", + "tags": [], + "label": "title", + "description": [], + "path": "packages/core/chrome/core-chrome-browser/src/nav_controls.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-common.ChromeHelpMenuLink.href", + "type": "string", + "tags": [], + "label": "href", + "description": [], + "path": "packages/core/chrome/core-chrome-browser/src/nav_controls.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-common.ChromeHelpMenuLink.iconType", + "type": "string", + "tags": [], + "label": "iconType", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/chrome/core-chrome-browser/src/nav_controls.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-chrome-browser", "id": "def-common.ChromeNavControl", @@ -941,6 +991,55 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-common.ChromeNavControls.setHelpMenuLinks", + "type": "Function", + "tags": [], + "label": "setHelpMenuLinks", + "description": [ + "Set the help menu links" + ], + "signature": [ + "(links: ", + { + "pluginId": "@kbn/core-chrome-browser", + "scope": "common", + "docId": "kibKbnCoreChromeBrowserPluginApi", + "section": "def-common.ChromeHelpMenuLink", + "text": "ChromeHelpMenuLink" + }, + "[]) => void" + ], + "path": "packages/core/chrome/core-chrome-browser/src/nav_controls.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-common.ChromeNavControls.setHelpMenuLinks.$1", + "type": "Array", + "tags": [], + "label": "links", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-chrome-browser", + "scope": "common", + "docId": "kibKbnCoreChromeBrowserPluginApi", + "section": "def-common.ChromeHelpMenuLink", + "text": "ChromeHelpMenuLink" + }, + "[]" + ], + "path": "packages/core/chrome/core-chrome-browser/src/nav_controls.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] } ], "initialIsOpen": false @@ -2227,6 +2326,55 @@ ], "returnComment": [] }, + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-common.ChromeStart.setHelpMenuLinks", + "type": "Function", + "tags": [], + "label": "setHelpMenuLinks", + "description": [ + "\nOverride the default links shown in the help menu" + ], + "signature": [ + "(links: ", + { + "pluginId": "@kbn/core-chrome-browser", + "scope": "common", + "docId": "kibKbnCoreChromeBrowserPluginApi", + "section": "def-common.ChromeHelpMenuLink", + "text": "ChromeHelpMenuLink" + }, + "[]) => void" + ], + "path": "packages/core/chrome/core-chrome-browser/src/contracts.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-common.ChromeStart.setHelpMenuLinks.$1", + "type": "Array", + "tags": [], + "label": "links", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-chrome-browser", + "scope": "common", + "docId": "kibKbnCoreChromeBrowserPluginApi", + "section": "def-common.ChromeHelpMenuLink", + "text": "ChromeHelpMenuLink" + }, + "[]" + ], + "path": "packages/core/chrome/core-chrome-browser/src/contracts.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, { "parentPluginId": "@kbn/core-chrome-browser", "id": "def-common.ChromeStart.getGlobalHelpExtensionMenuLinks$", @@ -2416,6 +2564,26 @@ ], "returnComment": [] }, + { + "parentPluginId": "@kbn/core-chrome-browser", + "id": "def-common.ChromeStart.getHelpSupportUrl$", + "type": "Function", + "tags": [], + "label": "getHelpSupportUrl$", + "description": [ + "\nGet the support URL shown in the help menu" + ], + "signature": [ + "() => ", + "Observable", + "" + ], + "path": "packages/core/chrome/core-chrome-browser/src/contracts.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, { "parentPluginId": "@kbn/core-chrome-browser", "id": "def-common.ChromeStart.getIsNavDrawerLocked$", diff --git a/api_docs/kbn_core_chrome_browser.mdx b/api_docs/kbn_core_chrome_browser.mdx index a98f34d29414a..d18ed9db795cc 100644 --- a/api_docs/kbn_core_chrome_browser.mdx +++ b/api_docs/kbn_core_chrome_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser title: "@kbn/core-chrome-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser'] --- import kbnCoreChromeBrowserObj from './kbn_core_chrome_browser.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 157 | 0 | 61 | 0 | +| 166 | 0 | 67 | 0 | ## Common diff --git a/api_docs/kbn_core_chrome_browser_mocks.mdx b/api_docs/kbn_core_chrome_browser_mocks.mdx index 9971dd240ee01..3f904a46cf215 100644 --- a/api_docs/kbn_core_chrome_browser_mocks.mdx +++ b/api_docs/kbn_core_chrome_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser-mocks title: "@kbn/core-chrome-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser-mocks'] --- import kbnCoreChromeBrowserMocksObj from './kbn_core_chrome_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_config_server_internal.mdx b/api_docs/kbn_core_config_server_internal.mdx index 73f6d07157fc6..36f2c3f8d4211 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-config-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-config-server-internal'] --- import kbnCoreConfigServerInternalObj from './kbn_core_config_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser.mdx b/api_docs/kbn_core_custom_branding_browser.mdx index 4dd4c46bdaf4c..b13cbd06d06ae 100644 --- a/api_docs/kbn_core_custom_branding_browser.mdx +++ b/api_docs/kbn_core_custom_branding_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser title: "@kbn/core-custom-branding-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser'] --- import kbnCoreCustomBrandingBrowserObj from './kbn_core_custom_branding_browser.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_internal.mdx b/api_docs/kbn_core_custom_branding_browser_internal.mdx index a3eb72d395357..07255a61ef8ee 100644 --- a/api_docs/kbn_core_custom_branding_browser_internal.mdx +++ b/api_docs/kbn_core_custom_branding_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-internal title: "@kbn/core-custom-branding-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-internal'] --- import kbnCoreCustomBrandingBrowserInternalObj from './kbn_core_custom_branding_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_mocks.mdx b/api_docs/kbn_core_custom_branding_browser_mocks.mdx index f56893f9807c8..d43b547f61936 100644 --- a/api_docs/kbn_core_custom_branding_browser_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-mocks title: "@kbn/core-custom-branding-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-mocks'] --- import kbnCoreCustomBrandingBrowserMocksObj from './kbn_core_custom_branding_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_common.mdx b/api_docs/kbn_core_custom_branding_common.mdx index 27a4e3f248786..574213ae5c3c5 100644 --- a/api_docs/kbn_core_custom_branding_common.mdx +++ b/api_docs/kbn_core_custom_branding_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-common title: "@kbn/core-custom-branding-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-common plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-common'] --- import kbnCoreCustomBrandingCommonObj from './kbn_core_custom_branding_common.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server.mdx b/api_docs/kbn_core_custom_branding_server.mdx index 9fc4ff8f05bc2..d3f84a7278372 100644 --- a/api_docs/kbn_core_custom_branding_server.mdx +++ b/api_docs/kbn_core_custom_branding_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server title: "@kbn/core-custom-branding-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server'] --- import kbnCoreCustomBrandingServerObj from './kbn_core_custom_branding_server.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_internal.mdx b/api_docs/kbn_core_custom_branding_server_internal.mdx index 0f2bdd55f8b34..ea12c1ad4746a 100644 --- a/api_docs/kbn_core_custom_branding_server_internal.mdx +++ b/api_docs/kbn_core_custom_branding_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-internal title: "@kbn/core-custom-branding-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-internal'] --- import kbnCoreCustomBrandingServerInternalObj from './kbn_core_custom_branding_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_mocks.mdx b/api_docs/kbn_core_custom_branding_server_mocks.mdx index c13ea492a7c12..a40642519f373 100644 --- a/api_docs/kbn_core_custom_branding_server_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-mocks title: "@kbn/core-custom-branding-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-mocks'] --- import kbnCoreCustomBrandingServerMocksObj from './kbn_core_custom_branding_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index 705af1a99f7e6..f4e6020861c2c 100644 --- a/api_docs/kbn_core_deprecations_browser.mdx +++ b/api_docs/kbn_core_deprecations_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser title: "@kbn/core-deprecations-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser'] --- import kbnCoreDeprecationsBrowserObj from './kbn_core_deprecations_browser.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_internal.mdx b/api_docs/kbn_core_deprecations_browser_internal.mdx index fcdb41ea8ec35..4aad2c2eb3045 100644 --- a/api_docs/kbn_core_deprecations_browser_internal.mdx +++ b/api_docs/kbn_core_deprecations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-internal title: "@kbn/core-deprecations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-internal'] --- import kbnCoreDeprecationsBrowserInternalObj from './kbn_core_deprecations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_mocks.mdx b/api_docs/kbn_core_deprecations_browser_mocks.mdx index 48bad509c8b75..d6ea7c1ad0998 100644 --- a/api_docs/kbn_core_deprecations_browser_mocks.mdx +++ b/api_docs/kbn_core_deprecations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-mocks title: "@kbn/core-deprecations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-mocks'] --- import kbnCoreDeprecationsBrowserMocksObj from './kbn_core_deprecations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_common.mdx b/api_docs/kbn_core_deprecations_common.mdx index 2a3d480017da6..c9f3ffa34e420 100644 --- a/api_docs/kbn_core_deprecations_common.mdx +++ b/api_docs/kbn_core_deprecations_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-common title: "@kbn/core-deprecations-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-common plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-common'] --- import kbnCoreDeprecationsCommonObj from './kbn_core_deprecations_common.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server.mdx b/api_docs/kbn_core_deprecations_server.mdx index 3adc0a462aec1..a1746de33122b 100644 --- a/api_docs/kbn_core_deprecations_server.mdx +++ b/api_docs/kbn_core_deprecations_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server title: "@kbn/core-deprecations-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server'] --- import kbnCoreDeprecationsServerObj from './kbn_core_deprecations_server.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_internal.mdx b/api_docs/kbn_core_deprecations_server_internal.mdx index 19b6c3861807f..79c322f80b0d5 100644 --- a/api_docs/kbn_core_deprecations_server_internal.mdx +++ b/api_docs/kbn_core_deprecations_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-internal title: "@kbn/core-deprecations-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-internal'] --- import kbnCoreDeprecationsServerInternalObj from './kbn_core_deprecations_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_mocks.mdx b/api_docs/kbn_core_deprecations_server_mocks.mdx index 7fec2cfc9e9a7..a816923aade61 100644 --- a/api_docs/kbn_core_deprecations_server_mocks.mdx +++ b/api_docs/kbn_core_deprecations_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-mocks title: "@kbn/core-deprecations-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-mocks'] --- import kbnCoreDeprecationsServerMocksObj from './kbn_core_deprecations_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser.mdx b/api_docs/kbn_core_doc_links_browser.mdx index 34fa37a32b791..1672f37050fde 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser'] --- import kbnCoreDocLinksBrowserObj from './kbn_core_doc_links_browser.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser_mocks.mdx b/api_docs/kbn_core_doc_links_browser_mocks.mdx index 14b71b7706477..9b4d14ae1b7cd 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks title: "@kbn/core-doc-links-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser-mocks'] --- import kbnCoreDocLinksBrowserMocksObj from './kbn_core_doc_links_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server.mdx b/api_docs/kbn_core_doc_links_server.mdx index 39efaa80587c8..0565c0ab62ed2 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server'] --- import kbnCoreDocLinksServerObj from './kbn_core_doc_links_server.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server_mocks.mdx b/api_docs/kbn_core_doc_links_server_mocks.mdx index 85c2fb294ecbf..8389ce5249656 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks title: "@kbn/core-doc-links-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server-mocks'] --- import kbnCoreDocLinksServerMocksObj from './kbn_core_doc_links_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx index c989fe7b121fa..dd0d1aaad67dd 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-internal title: "@kbn/core-elasticsearch-client-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-internal'] --- import kbnCoreElasticsearchClientServerInternalObj from './kbn_core_elasticsearch_client_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx index e9301439b6ed0..0f5abe010369c 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-mocks title: "@kbn/core-elasticsearch-client-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-mocks'] --- import kbnCoreElasticsearchClientServerMocksObj from './kbn_core_elasticsearch_client_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server.mdx b/api_docs/kbn_core_elasticsearch_server.mdx index c6702cd7c050f..5bfec1ca6c4b3 100644 --- a/api_docs/kbn_core_elasticsearch_server.mdx +++ b/api_docs/kbn_core_elasticsearch_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server title: "@kbn/core-elasticsearch-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server'] --- import kbnCoreElasticsearchServerObj from './kbn_core_elasticsearch_server.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_internal.mdx b/api_docs/kbn_core_elasticsearch_server_internal.mdx index d135ff737655e..e7feab4db2118 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-internal title: "@kbn/core-elasticsearch-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-internal'] --- import kbnCoreElasticsearchServerInternalObj from './kbn_core_elasticsearch_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_server_mocks.mdx index 8d7e4fdb80df1..b78a3adba1397 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-mocks title: "@kbn/core-elasticsearch-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-mocks'] --- import kbnCoreElasticsearchServerMocksObj from './kbn_core_elasticsearch_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_internal.mdx b/api_docs/kbn_core_environment_server_internal.mdx index a0cbc63237698..84154ebfde089 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-internal'] --- import kbnCoreEnvironmentServerInternalObj from './kbn_core_environment_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_mocks.mdx b/api_docs/kbn_core_environment_server_mocks.mdx index 85b2b29711d5e..e47082f7c1934 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-mocks'] --- import kbnCoreEnvironmentServerMocksObj from './kbn_core_environment_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser.mdx b/api_docs/kbn_core_execution_context_browser.mdx index 9a4bff3292463..36e70d152ebfb 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser'] --- import kbnCoreExecutionContextBrowserObj from './kbn_core_execution_context_browser.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_internal.mdx b/api_docs/kbn_core_execution_context_browser_internal.mdx index 44fa0c99cac00..80282eb1ff5fc 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-internal title: "@kbn/core-execution-context-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-internal'] --- import kbnCoreExecutionContextBrowserInternalObj from './kbn_core_execution_context_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_mocks.mdx b/api_docs/kbn_core_execution_context_browser_mocks.mdx index b6fb4bb8b7abf..60d57dd5e3e38 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-mocks title: "@kbn/core-execution-context-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-mocks'] --- import kbnCoreExecutionContextBrowserMocksObj from './kbn_core_execution_context_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_common.mdx b/api_docs/kbn_core_execution_context_common.mdx index c2ca321287b08..3f958fd4c0e4a 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-common plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-common'] --- import kbnCoreExecutionContextCommonObj from './kbn_core_execution_context_common.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server.mdx b/api_docs/kbn_core_execution_context_server.mdx index 33a8a9c730e4e..5f493fcce34cd 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server'] --- import kbnCoreExecutionContextServerObj from './kbn_core_execution_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_internal.mdx b/api_docs/kbn_core_execution_context_server_internal.mdx index d86a751f18d6d..d6aad01e676eb 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-internal title: "@kbn/core-execution-context-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-internal'] --- import kbnCoreExecutionContextServerInternalObj from './kbn_core_execution_context_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_mocks.mdx b/api_docs/kbn_core_execution_context_server_mocks.mdx index 6b3d182746dd0..cd4b201905a62 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-mocks title: "@kbn/core-execution-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-mocks'] --- import kbnCoreExecutionContextServerMocksObj from './kbn_core_execution_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser.mdx b/api_docs/kbn_core_fatal_errors_browser.mdx index 8d6ec4e81ace1..4eefbbc1a6212 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser'] --- import kbnCoreFatalErrorsBrowserObj from './kbn_core_fatal_errors_browser.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx index 94604c98e9a1d..7202e3113edc7 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser-mocks title: "@kbn/core-fatal-errors-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser-mocks'] --- import kbnCoreFatalErrorsBrowserMocksObj from './kbn_core_fatal_errors_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index 57198beac077e..35333e0495976 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser'] --- import kbnCoreHttpBrowserObj from './kbn_core_http_browser.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_internal.mdx b/api_docs/kbn_core_http_browser_internal.mdx index a769b60950ea0..53697d0e06fa5 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-internal'] --- import kbnCoreHttpBrowserInternalObj from './kbn_core_http_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_mocks.mdx b/api_docs/kbn_core_http_browser_mocks.mdx index 6830558759b94..99d48d422df00 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-mocks'] --- import kbnCoreHttpBrowserMocksObj from './kbn_core_http_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_common.mdx b/api_docs/kbn_core_http_common.mdx index 7f4e3aa97d4fa..fffc5966c181f 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-common plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-common'] --- import kbnCoreHttpCommonObj from './kbn_core_http_common.devdocs.json'; diff --git a/api_docs/kbn_core_http_context_server_mocks.mdx b/api_docs/kbn_core_http_context_server_mocks.mdx index 44e850dd73eaf..6b1abf77a61ad 100644 --- a/api_docs/kbn_core_http_context_server_mocks.mdx +++ b/api_docs/kbn_core_http_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-context-server-mocks title: "@kbn/core-http-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-context-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-context-server-mocks'] --- import kbnCoreHttpContextServerMocksObj from './kbn_core_http_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_request_handler_context_server.mdx b/api_docs/kbn_core_http_request_handler_context_server.mdx index 58c73ddf9155f..e2d870af2bd90 100644 --- a/api_docs/kbn_core_http_request_handler_context_server.mdx +++ b/api_docs/kbn_core_http_request_handler_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-request-handler-context-server title: "@kbn/core-http-request-handler-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-request-handler-context-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-request-handler-context-server'] --- import kbnCoreHttpRequestHandlerContextServerObj from './kbn_core_http_request_handler_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server.mdx b/api_docs/kbn_core_http_resources_server.mdx index 0e251aca15b30..558e64209ae58 100644 --- a/api_docs/kbn_core_http_resources_server.mdx +++ b/api_docs/kbn_core_http_resources_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server title: "@kbn/core-http-resources-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server'] --- import kbnCoreHttpResourcesServerObj from './kbn_core_http_resources_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_internal.mdx b/api_docs/kbn_core_http_resources_server_internal.mdx index 104637ea97997..9302f4d7edfb9 100644 --- a/api_docs/kbn_core_http_resources_server_internal.mdx +++ b/api_docs/kbn_core_http_resources_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-internal title: "@kbn/core-http-resources-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-internal'] --- import kbnCoreHttpResourcesServerInternalObj from './kbn_core_http_resources_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_mocks.mdx b/api_docs/kbn_core_http_resources_server_mocks.mdx index e36512c4a18f1..b49b267aa07d3 100644 --- a/api_docs/kbn_core_http_resources_server_mocks.mdx +++ b/api_docs/kbn_core_http_resources_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-mocks title: "@kbn/core-http-resources-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-mocks'] --- import kbnCoreHttpResourcesServerMocksObj from './kbn_core_http_resources_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index 89d0c5b958ecc..ff78a784afc47 100644 --- a/api_docs/kbn_core_http_router_server_internal.mdx +++ b/api_docs/kbn_core_http_router_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-internal title: "@kbn/core-http-router-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-internal'] --- import kbnCoreHttpRouterServerInternalObj from './kbn_core_http_router_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_mocks.mdx b/api_docs/kbn_core_http_router_server_mocks.mdx index 8b8c401974e25..44e99bcd360f1 100644 --- a/api_docs/kbn_core_http_router_server_mocks.mdx +++ b/api_docs/kbn_core_http_router_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-mocks title: "@kbn/core-http-router-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-mocks'] --- import kbnCoreHttpRouterServerMocksObj from './kbn_core_http_router_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_server.devdocs.json b/api_docs/kbn_core_http_server.devdocs.json index 92518c2c6c986..cdc4974ac790f 100644 --- a/api_docs/kbn_core_http_server.devdocs.json +++ b/api_docs/kbn_core_http_server.devdocs.json @@ -3739,10 +3739,6 @@ "plugin": "cloudSecurityPosture", "path": "x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/vulnerabilities_dashboard.ts" }, - { - "plugin": "cloudSecurityPosture", - "path": "x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts" - }, { "plugin": "cloudSecurityPosture", "path": "x-pack/plugins/cloud_security_posture/server/routes/status/status.ts" @@ -4295,70 +4291,6 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.ts" }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/live_query/get_live_query_details_route.ts" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/live_query/get_live_query_results_route.ts" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/live_query/find_live_query_route.ts" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/saved_query/find_saved_query_route.ts" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/saved_query/read_saved_query_route.ts" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/status/create_status_route.ts" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policies.ts" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policy.ts" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_status_for_agent_policy.ts" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/fleet_wrapper/get_package_policies.ts" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agents.ts" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_details.ts" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/pack/find_pack_route.ts" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/pack/read_pack_route.ts" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/privileges_check/privileges_check_route.ts" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/asset/get_assets_status_route.ts" - }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/find_rules/route.ts" @@ -5480,16 +5412,8 @@ "path": "x-pack/plugins/cloud_integrations/cloud_chat/server/routes/chat.test.ts" }, { - "plugin": "cloudSecurityPosture", - "path": "x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts" - }, - { - "plugin": "cloudSecurityPosture", - "path": "x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts" - }, - { - "plugin": "cloudSecurityPosture", - "path": "x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts" + "plugin": "fleet", + "path": "x-pack/plugins/fleet/server/routes/uninstall_token/handlers.test.ts" }, { "plugin": "fleet", @@ -5819,6 +5743,10 @@ "plugin": "fleet", "path": "x-pack/plugins/fleet/server/routes/uninstall_token/index.ts" }, + { + "plugin": "fleet", + "path": "x-pack/plugins/fleet/server/routes/uninstall_token/index.ts" + }, { "plugin": "fleet", "path": "x-pack/plugins/fleet/server/services/security/fleet_router.test.ts" @@ -7049,22 +6977,6 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/detection_engine_health/setup/setup_health_route.ts" }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/live_query/create_live_query_route.ts" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/saved_query/create_saved_query_route.ts" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/pack/create_pack_route.ts" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/asset/update_assets_route.ts" - }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/api/create_legacy_notification/route.ts" @@ -8887,14 +8799,6 @@ "plugin": "lists", "path": "x-pack/plugins/lists/server/routes/update_list_route.ts" }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/saved_query/update_saved_query_route.ts" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/pack/update_pack_route.ts" - }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.ts" @@ -9975,14 +9879,6 @@ "plugin": "lists", "path": "x-pack/plugins/lists/server/routes/delete_list_route.ts" }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/saved_query/delete_saved_query_route.ts" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/server/routes/pack/delete_pack_route.ts" - }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_delete_rules/route.ts" @@ -14126,10 +14022,78 @@ "plugin": "cloudSecurityPosture", "path": "x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts" }, + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts" + }, { "plugin": "cloudSecurityPosture", "path": "x-pack/plugins/cloud_security_posture/server/routes/csp_rule_template/get_csp_rule_template.ts" }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/live_query/get_live_query_details_route.ts" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/live_query/get_live_query_results_route.ts" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/live_query/find_live_query_route.ts" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/saved_query/find_saved_query_route.ts" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/saved_query/read_saved_query_route.ts" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/status/create_status_route.ts" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policies.ts" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policy.ts" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_status_for_agent_policy.ts" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/fleet_wrapper/get_package_policies.ts" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agents.ts" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_details.ts" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/pack/find_pack_route.ts" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/pack/read_pack_route.ts" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/privileges_check/privileges_check_route.ts" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/asset/get_assets_status_route.ts" + }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts" @@ -14362,6 +14326,18 @@ "plugin": "cloudDefend", "path": "x-pack/plugins/cloud_defend/server/routes/status/status.test.ts" }, + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts" + }, + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts" + }, + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts" + }, { "plugin": "cloudSecurityPosture", "path": "x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.test.ts" @@ -14530,6 +14506,14 @@ "plugin": "canvas", "path": "x-pack/plugins/canvas/server/routes/workpad/update.ts" }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/saved_query/update_saved_query_route.ts" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/pack/update_pack_route.ts" + }, { "plugin": "transform", "path": "x-pack/plugins/transform/server/routes/api/transforms.ts" @@ -15010,6 +14994,22 @@ "plugin": "canvas", "path": "x-pack/plugins/canvas/server/routes/workpad/import.ts" }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/live_query/create_live_query_route.ts" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/saved_query/create_saved_query_route.ts" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/pack/create_pack_route.ts" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/asset/update_assets_route.ts" + }, { "plugin": "unifiedSearch", "path": "src/plugins/unified_search/server/autocomplete/value_suggestions_route.ts" @@ -15378,6 +15378,14 @@ "plugin": "canvas", "path": "x-pack/plugins/canvas/server/routes/workpad/delete.ts" }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/saved_query/delete_saved_query_route.ts" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/server/routes/pack/delete_pack_route.ts" + }, { "plugin": "maps", "path": "x-pack/plugins/maps/server/data_indexing/indexing_routes.ts" diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index c8ce637609a8d..9c8f8292e9203 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index 37152419ec61d..fc90a923127ec 100644 --- a/api_docs/kbn_core_http_server_internal.mdx +++ b/api_docs/kbn_core_http_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-internal title: "@kbn/core-http-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-internal'] --- import kbnCoreHttpServerInternalObj from './kbn_core_http_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_mocks.mdx b/api_docs/kbn_core_http_server_mocks.mdx index 38b996d68e19e..fca10b2470a7b 100644 --- a/api_docs/kbn_core_http_server_mocks.mdx +++ b/api_docs/kbn_core_http_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-mocks title: "@kbn/core-http-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-mocks'] --- import kbnCoreHttpServerMocksObj from './kbn_core_http_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser.mdx b/api_docs/kbn_core_i18n_browser.mdx index 384056aae6faa..da45a2b7a3937 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser'] --- import kbnCoreI18nBrowserObj from './kbn_core_i18n_browser.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser_mocks.mdx b/api_docs/kbn_core_i18n_browser_mocks.mdx index a146a9355e538..b16574938c496 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser-mocks'] --- import kbnCoreI18nBrowserMocksObj from './kbn_core_i18n_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server.mdx b/api_docs/kbn_core_i18n_server.mdx index 48738f858e1db..e3e029d65d6d6 100644 --- a/api_docs/kbn_core_i18n_server.mdx +++ b/api_docs/kbn_core_i18n_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server title: "@kbn/core-i18n-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server'] --- import kbnCoreI18nServerObj from './kbn_core_i18n_server.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_internal.mdx b/api_docs/kbn_core_i18n_server_internal.mdx index 31ce4c84da6bb..6d281f60d24b6 100644 --- a/api_docs/kbn_core_i18n_server_internal.mdx +++ b/api_docs/kbn_core_i18n_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-internal title: "@kbn/core-i18n-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-internal'] --- import kbnCoreI18nServerInternalObj from './kbn_core_i18n_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_mocks.mdx b/api_docs/kbn_core_i18n_server_mocks.mdx index 3e0175adb9a29..df1e020523f62 100644 --- a/api_docs/kbn_core_i18n_server_mocks.mdx +++ b/api_docs/kbn_core_i18n_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-mocks title: "@kbn/core-i18n-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-mocks'] --- import kbnCoreI18nServerMocksObj from './kbn_core_i18n_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx index c2e1506137554..0ad4d0f2ec0ed 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks title: "@kbn/core-injected-metadata-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser-mocks'] --- import kbnCoreInjectedMetadataBrowserMocksObj from './kbn_core_injected_metadata_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_internal.mdx b/api_docs/kbn_core_integrations_browser_internal.mdx index 08d4413e6f96a..9a81492fe1975 100644 --- a/api_docs/kbn_core_integrations_browser_internal.mdx +++ b/api_docs/kbn_core_integrations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-internal title: "@kbn/core-integrations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-internal'] --- import kbnCoreIntegrationsBrowserInternalObj from './kbn_core_integrations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_mocks.mdx b/api_docs/kbn_core_integrations_browser_mocks.mdx index ca3a0ec2fc9fc..3ec3daae38115 100644 --- a/api_docs/kbn_core_integrations_browser_mocks.mdx +++ b/api_docs/kbn_core_integrations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-mocks title: "@kbn/core-integrations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-mocks'] --- import kbnCoreIntegrationsBrowserMocksObj from './kbn_core_integrations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser.mdx b/api_docs/kbn_core_lifecycle_browser.mdx index fbc58e07cc3c6..212298f954c98 100644 --- a/api_docs/kbn_core_lifecycle_browser.mdx +++ b/api_docs/kbn_core_lifecycle_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser title: "@kbn/core-lifecycle-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser'] --- import kbnCoreLifecycleBrowserObj from './kbn_core_lifecycle_browser.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser_mocks.mdx b/api_docs/kbn_core_lifecycle_browser_mocks.mdx index b1fa75a0c7840..626ce47187eac 100644 --- a/api_docs/kbn_core_lifecycle_browser_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser-mocks title: "@kbn/core-lifecycle-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser-mocks'] --- import kbnCoreLifecycleBrowserMocksObj from './kbn_core_lifecycle_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server.mdx b/api_docs/kbn_core_lifecycle_server.mdx index 19837d2466be7..e2830ae5cf016 100644 --- a/api_docs/kbn_core_lifecycle_server.mdx +++ b/api_docs/kbn_core_lifecycle_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server title: "@kbn/core-lifecycle-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server'] --- import kbnCoreLifecycleServerObj from './kbn_core_lifecycle_server.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server_mocks.mdx b/api_docs/kbn_core_lifecycle_server_mocks.mdx index 27dba6e73fc29..64e32ba8edc67 100644 --- a/api_docs/kbn_core_lifecycle_server_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server-mocks title: "@kbn/core-lifecycle-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server-mocks'] --- import kbnCoreLifecycleServerMocksObj from './kbn_core_lifecycle_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_browser_mocks.mdx b/api_docs/kbn_core_logging_browser_mocks.mdx index 92a43ea8687d5..e461b3fb20f9a 100644 --- a/api_docs/kbn_core_logging_browser_mocks.mdx +++ b/api_docs/kbn_core_logging_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-browser-mocks title: "@kbn/core-logging-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-browser-mocks'] --- import kbnCoreLoggingBrowserMocksObj from './kbn_core_logging_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_common_internal.mdx b/api_docs/kbn_core_logging_common_internal.mdx index 263c754aa7684..34b1a1178c5d7 100644 --- a/api_docs/kbn_core_logging_common_internal.mdx +++ b/api_docs/kbn_core_logging_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-common-internal title: "@kbn/core-logging-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-common-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-common-internal'] --- import kbnCoreLoggingCommonInternalObj from './kbn_core_logging_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index f5bb802aa6c40..f3236e8121fdd 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server'] --- import kbnCoreLoggingServerObj from './kbn_core_logging_server.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_internal.mdx b/api_docs/kbn_core_logging_server_internal.mdx index 2dba6b6673c5d..73adce21cdcf7 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-internal'] --- import kbnCoreLoggingServerInternalObj from './kbn_core_logging_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_mocks.mdx b/api_docs/kbn_core_logging_server_mocks.mdx index 45ffe9cf4d651..d28da30ecdd5d 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-mocks'] --- import kbnCoreLoggingServerMocksObj from './kbn_core_logging_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_internal.mdx b/api_docs/kbn_core_metrics_collectors_server_internal.mdx index 837c4e7df325f..96963764a6763 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-internal title: "@kbn/core-metrics-collectors-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-internal'] --- import kbnCoreMetricsCollectorsServerInternalObj from './kbn_core_metrics_collectors_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx index d2840e4e3968b..8c75dde632791 100644 --- a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-mocks title: "@kbn/core-metrics-collectors-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-mocks'] --- import kbnCoreMetricsCollectorsServerMocksObj from './kbn_core_metrics_collectors_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server.mdx b/api_docs/kbn_core_metrics_server.mdx index 8df5f813b9768..52153d6ff57da 100644 --- a/api_docs/kbn_core_metrics_server.mdx +++ b/api_docs/kbn_core_metrics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server title: "@kbn/core-metrics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server'] --- import kbnCoreMetricsServerObj from './kbn_core_metrics_server.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_internal.mdx b/api_docs/kbn_core_metrics_server_internal.mdx index af792e2945fc4..00a6e9c8ad6d0 100644 --- a/api_docs/kbn_core_metrics_server_internal.mdx +++ b/api_docs/kbn_core_metrics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-internal title: "@kbn/core-metrics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-internal'] --- import kbnCoreMetricsServerInternalObj from './kbn_core_metrics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_mocks.mdx b/api_docs/kbn_core_metrics_server_mocks.mdx index e41644eb284f5..a4c20122cb118 100644 --- a/api_docs/kbn_core_metrics_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-mocks title: "@kbn/core-metrics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-mocks'] --- import kbnCoreMetricsServerMocksObj from './kbn_core_metrics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_mount_utils_browser.mdx b/api_docs/kbn_core_mount_utils_browser.mdx index 084ba973a9e70..7f21472bb92e0 100644 --- a/api_docs/kbn_core_mount_utils_browser.mdx +++ b/api_docs/kbn_core_mount_utils_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser title: "@kbn/core-mount-utils-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-mount-utils-browser plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-mount-utils-browser'] --- import kbnCoreMountUtilsBrowserObj from './kbn_core_mount_utils_browser.devdocs.json'; diff --git a/api_docs/kbn_core_node_server.mdx b/api_docs/kbn_core_node_server.mdx index 9d2c1bbc4f07c..909233cc5ab6f 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server'] --- import kbnCoreNodeServerObj from './kbn_core_node_server.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_internal.mdx b/api_docs/kbn_core_node_server_internal.mdx index 5b265d7f20e05..bef05ac9357c3 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-internal'] --- import kbnCoreNodeServerInternalObj from './kbn_core_node_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_mocks.mdx b/api_docs/kbn_core_node_server_mocks.mdx index a3a7f979c4a37..8d3df740e56ff 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-mocks'] --- import kbnCoreNodeServerMocksObj from './kbn_core_node_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser.mdx b/api_docs/kbn_core_notifications_browser.mdx index d1a80ab0625fc..bb98f55ea0c41 100644 --- a/api_docs/kbn_core_notifications_browser.mdx +++ b/api_docs/kbn_core_notifications_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser title: "@kbn/core-notifications-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser'] --- import kbnCoreNotificationsBrowserObj from './kbn_core_notifications_browser.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_internal.mdx b/api_docs/kbn_core_notifications_browser_internal.mdx index dc00617d5b379..8e6d151c4d659 100644 --- a/api_docs/kbn_core_notifications_browser_internal.mdx +++ b/api_docs/kbn_core_notifications_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-internal title: "@kbn/core-notifications-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-internal'] --- import kbnCoreNotificationsBrowserInternalObj from './kbn_core_notifications_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_mocks.mdx b/api_docs/kbn_core_notifications_browser_mocks.mdx index 559a39395b091..313654df45a85 100644 --- a/api_docs/kbn_core_notifications_browser_mocks.mdx +++ b/api_docs/kbn_core_notifications_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-mocks title: "@kbn/core-notifications-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-mocks'] --- import kbnCoreNotificationsBrowserMocksObj from './kbn_core_notifications_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser.mdx b/api_docs/kbn_core_overlays_browser.mdx index 6ad21beb9fbb2..772ceeee94c36 100644 --- a/api_docs/kbn_core_overlays_browser.mdx +++ b/api_docs/kbn_core_overlays_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser title: "@kbn/core-overlays-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser'] --- import kbnCoreOverlaysBrowserObj from './kbn_core_overlays_browser.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_internal.mdx b/api_docs/kbn_core_overlays_browser_internal.mdx index 6a923c38874b6..50f5127ccc679 100644 --- a/api_docs/kbn_core_overlays_browser_internal.mdx +++ b/api_docs/kbn_core_overlays_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-internal title: "@kbn/core-overlays-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-internal'] --- import kbnCoreOverlaysBrowserInternalObj from './kbn_core_overlays_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_mocks.mdx b/api_docs/kbn_core_overlays_browser_mocks.mdx index 3d0bb2d7f0e3c..efe823a173a12 100644 --- a/api_docs/kbn_core_overlays_browser_mocks.mdx +++ b/api_docs/kbn_core_overlays_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-mocks title: "@kbn/core-overlays-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-mocks'] --- import kbnCoreOverlaysBrowserMocksObj from './kbn_core_overlays_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser.mdx b/api_docs/kbn_core_plugins_browser.mdx index 011a4a7ec2fd5..d9e879a748dc3 100644 --- a/api_docs/kbn_core_plugins_browser.mdx +++ b/api_docs/kbn_core_plugins_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser title: "@kbn/core-plugins-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser'] --- import kbnCorePluginsBrowserObj from './kbn_core_plugins_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser_mocks.mdx b/api_docs/kbn_core_plugins_browser_mocks.mdx index 4b57b1c67b92a..602278778c3e6 100644 --- a/api_docs/kbn_core_plugins_browser_mocks.mdx +++ b/api_docs/kbn_core_plugins_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser-mocks title: "@kbn/core-plugins-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser-mocks'] --- import kbnCorePluginsBrowserMocksObj from './kbn_core_plugins_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server.mdx b/api_docs/kbn_core_plugins_server.mdx index dcc5f9bce15e8..15fadabe771b3 100644 --- a/api_docs/kbn_core_plugins_server.mdx +++ b/api_docs/kbn_core_plugins_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server title: "@kbn/core-plugins-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server'] --- import kbnCorePluginsServerObj from './kbn_core_plugins_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server_mocks.mdx b/api_docs/kbn_core_plugins_server_mocks.mdx index 81327250270c9..ecd45f7308de0 100644 --- a/api_docs/kbn_core_plugins_server_mocks.mdx +++ b/api_docs/kbn_core_plugins_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server-mocks title: "@kbn/core-plugins-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server-mocks'] --- import kbnCorePluginsServerMocksObj from './kbn_core_plugins_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index b5088454b1d8a..8ce163e266509 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server'] --- import kbnCorePrebootServerObj from './kbn_core_preboot_server.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server_mocks.mdx b/api_docs/kbn_core_preboot_server_mocks.mdx index 22ee50388f0b0..054389789e925 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server-mocks'] --- import kbnCorePrebootServerMocksObj from './kbn_core_preboot_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_browser_mocks.mdx b/api_docs/kbn_core_rendering_browser_mocks.mdx index 4ae90668e74e6..18d62db4cc786 100644 --- a/api_docs/kbn_core_rendering_browser_mocks.mdx +++ b/api_docs/kbn_core_rendering_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-browser-mocks title: "@kbn/core-rendering-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-browser-mocks'] --- import kbnCoreRenderingBrowserMocksObj from './kbn_core_rendering_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_internal.mdx b/api_docs/kbn_core_rendering_server_internal.mdx index 9a54cf7cf3920..f2bff07a80180 100644 --- a/api_docs/kbn_core_rendering_server_internal.mdx +++ b/api_docs/kbn_core_rendering_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-internal title: "@kbn/core-rendering-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-internal'] --- import kbnCoreRenderingServerInternalObj from './kbn_core_rendering_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_mocks.mdx b/api_docs/kbn_core_rendering_server_mocks.mdx index b135ed45cbc2d..f55cdab569d6b 100644 --- a/api_docs/kbn_core_rendering_server_mocks.mdx +++ b/api_docs/kbn_core_rendering_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-mocks title: "@kbn/core-rendering-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-mocks'] --- import kbnCoreRenderingServerMocksObj from './kbn_core_rendering_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_root_server_internal.mdx b/api_docs/kbn_core_root_server_internal.mdx index b79f72ccb4974..7bcc25af5e680 100644 --- a/api_docs/kbn_core_root_server_internal.mdx +++ b/api_docs/kbn_core_root_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-root-server-internal title: "@kbn/core-root-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-root-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-root-server-internal'] --- import kbnCoreRootServerInternalObj from './kbn_core_root_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index 90d97d003d6b8..dc2c8b47d9168 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.mdx +++ b/api_docs/kbn_core_saved_objects_api_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-browser title: "@kbn/core-saved-objects-api-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-browser plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-browser'] --- import kbnCoreSavedObjectsApiBrowserObj from './kbn_core_saved_objects_api_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server.mdx b/api_docs/kbn_core_saved_objects_api_server.mdx index 5f3dae559b6d9..678967576150b 100644 --- a/api_docs/kbn_core_saved_objects_api_server.mdx +++ b/api_docs/kbn_core_saved_objects_api_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server title: "@kbn/core-saved-objects-api-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server'] --- import kbnCoreSavedObjectsApiServerObj from './kbn_core_saved_objects_api_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx index ce1c8b4561da9..fa67962a4b569 100644 --- a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-mocks title: "@kbn/core-saved-objects-api-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-mocks'] --- import kbnCoreSavedObjectsApiServerMocksObj from './kbn_core_saved_objects_api_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_internal.mdx b/api_docs/kbn_core_saved_objects_base_server_internal.mdx index 3c9f791e6f1bb..9dffcf9fef3f8 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-internal title: "@kbn/core-saved-objects-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-internal'] --- import kbnCoreSavedObjectsBaseServerInternalObj from './kbn_core_saved_objects_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx index 10017078ace48..6b9dbfe06b4c3 100644 --- a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-mocks title: "@kbn/core-saved-objects-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-mocks'] --- import kbnCoreSavedObjectsBaseServerMocksObj from './kbn_core_saved_objects_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser.mdx b/api_docs/kbn_core_saved_objects_browser.mdx index 2d148f9c34964..ee12e8cc98ddb 100644 --- a/api_docs/kbn_core_saved_objects_browser.mdx +++ b/api_docs/kbn_core_saved_objects_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser title: "@kbn/core-saved-objects-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser'] --- import kbnCoreSavedObjectsBrowserObj from './kbn_core_saved_objects_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_internal.mdx b/api_docs/kbn_core_saved_objects_browser_internal.mdx index c23757de2cf8f..f6fdabf1d6567 100644 --- a/api_docs/kbn_core_saved_objects_browser_internal.mdx +++ b/api_docs/kbn_core_saved_objects_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-internal title: "@kbn/core-saved-objects-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-internal'] --- import kbnCoreSavedObjectsBrowserInternalObj from './kbn_core_saved_objects_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_mocks.mdx b/api_docs/kbn_core_saved_objects_browser_mocks.mdx index f4256143bb941..3e05cafa5ddd4 100644 --- a/api_docs/kbn_core_saved_objects_browser_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-mocks title: "@kbn/core-saved-objects-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-mocks'] --- import kbnCoreSavedObjectsBrowserMocksObj from './kbn_core_saved_objects_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_common.mdx b/api_docs/kbn_core_saved_objects_common.mdx index 786f7ea994800..149fbedfff6a9 100644 --- a/api_docs/kbn_core_saved_objects_common.mdx +++ b/api_docs/kbn_core_saved_objects_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-common title: "@kbn/core-saved-objects-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-common plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-common'] --- import kbnCoreSavedObjectsCommonObj from './kbn_core_saved_objects_common.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx index 5f66640f4d31b..ea4258f65d0d3 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-internal title: "@kbn/core-saved-objects-import-export-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-internal'] --- import kbnCoreSavedObjectsImportExportServerInternalObj from './kbn_core_saved_objects_import_export_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx index 6112c22b2b6a4..a037d82b40549 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-mocks title: "@kbn/core-saved-objects-import-export-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-mocks'] --- import kbnCoreSavedObjectsImportExportServerMocksObj from './kbn_core_saved_objects_import_export_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_internal.devdocs.json b/api_docs/kbn_core_saved_objects_migration_server_internal.devdocs.json index 699397cdf48dc..3a6495d4c5078 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.devdocs.json +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.devdocs.json @@ -1335,7 +1335,7 @@ "tags": [], "label": "pickupUpdatedMappings", "description": [ - "\nPickup updated mappings by performing an update by query operation on all\ndocuments in the index. Returns a task ID which can be\ntracked for progress.\n" + "\nPickup updated mappings by performing an update by query operation on all\ndocuments matching the passed in query. Returns a task ID which can be\ntracked for progress.\n" ], "signature": [ "(client: ", @@ -1346,7 +1346,9 @@ "section": "def-common.ElasticsearchClient", "text": "ElasticsearchClient" }, - ", index: string, batchSize: number) => ", + ", index: string, batchSize: number, query?: ", + "QueryDslQueryContainer", + " | undefined) => ", "TaskEither", "<", "RetryableEsClientError", @@ -1414,6 +1416,22 @@ "deprecated": false, "trackAdoption": false, "isRequired": true + }, + { + "parentPluginId": "@kbn/core-saved-objects-migration-server-internal", + "id": "def-common.pickupUpdatedMappings.$4", + "type": "Object", + "tags": [], + "label": "query", + "description": [], + "signature": [ + "QueryDslQueryContainer", + " | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/pickup_updated_mappings.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false } ], "returnComment": [], @@ -1746,7 +1764,7 @@ "\nUpdates an index's mappings and runs an pickupUpdatedMappings task so that the mapping\nchanges are \"picked up\". Returns a taskId to track progress." ], "signature": [ - "({ client, index, mappings, batchSize, }: ", + "({ client, index, mappings, batchSize, query, }: ", "UpdateAndPickupMappingsParams", ") => ", "TaskEither", @@ -1765,7 +1783,7 @@ "id": "def-common.updateAndPickupMappings.$1", "type": "Object", "tags": [], - "label": "{\n client,\n index,\n mappings,\n batchSize,\n}", + "label": "{\n client,\n index,\n mappings,\n batchSize,\n query,\n}", "description": [], "signature": [ "UpdateAndPickupMappingsParams" diff --git a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx index b04305fac4f0d..f50bbb8616046 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-internal title: "@kbn/core-saved-objects-migration-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-internal'] --- import kbnCoreSavedObjectsMigrationServerInternalObj from './kbn_core_saved_objects_migration_server_internal.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 123 | 0 | 89 | 46 | +| 124 | 0 | 90 | 46 | ## Common diff --git a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx index d78a63e5b0821..a478edb319751 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-mocks title: "@kbn/core-saved-objects-migration-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-mocks'] --- import kbnCoreSavedObjectsMigrationServerMocksObj from './kbn_core_saved_objects_migration_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server.mdx b/api_docs/kbn_core_saved_objects_server.mdx index d99d4ea8528ca..35596d4d833c9 100644 --- a/api_docs/kbn_core_saved_objects_server.mdx +++ b/api_docs/kbn_core_saved_objects_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server title: "@kbn/core-saved-objects-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server'] --- import kbnCoreSavedObjectsServerObj from './kbn_core_saved_objects_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_internal.mdx b/api_docs/kbn_core_saved_objects_server_internal.mdx index b193ab1fff041..b0b5255227a36 100644 --- a/api_docs/kbn_core_saved_objects_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-internal title: "@kbn/core-saved-objects-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-internal'] --- import kbnCoreSavedObjectsServerInternalObj from './kbn_core_saved_objects_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_mocks.mdx b/api_docs/kbn_core_saved_objects_server_mocks.mdx index 1c1fe623094b0..7a0b0f68d5d32 100644 --- a/api_docs/kbn_core_saved_objects_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-mocks title: "@kbn/core-saved-objects-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-mocks'] --- import kbnCoreSavedObjectsServerMocksObj from './kbn_core_saved_objects_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_utils_server.mdx b/api_docs/kbn_core_saved_objects_utils_server.mdx index f25d9886ec4af..8d295836a6cf8 100644 --- a/api_docs/kbn_core_saved_objects_utils_server.mdx +++ b/api_docs/kbn_core_saved_objects_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-utils-server title: "@kbn/core-saved-objects-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-utils-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-utils-server'] --- import kbnCoreSavedObjectsUtilsServerObj from './kbn_core_saved_objects_utils_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx index 098b77d3a5be4..768599b5053bb 100644 --- a/api_docs/kbn_core_status_common.mdx +++ b/api_docs/kbn_core_status_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common title: "@kbn/core-status-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common'] --- import kbnCoreStatusCommonObj from './kbn_core_status_common.devdocs.json'; diff --git a/api_docs/kbn_core_status_common_internal.mdx b/api_docs/kbn_core_status_common_internal.mdx index f3b3cb0d5f2d3..511a84babbe8f 100644 --- a/api_docs/kbn_core_status_common_internal.mdx +++ b/api_docs/kbn_core_status_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common-internal title: "@kbn/core-status-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common-internal'] --- import kbnCoreStatusCommonInternalObj from './kbn_core_status_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server.mdx b/api_docs/kbn_core_status_server.mdx index b2615ef588e50..2fc2797f45d10 100644 --- a/api_docs/kbn_core_status_server.mdx +++ b/api_docs/kbn_core_status_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server title: "@kbn/core-status-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server'] --- import kbnCoreStatusServerObj from './kbn_core_status_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_internal.mdx b/api_docs/kbn_core_status_server_internal.mdx index 33a59a6da2ac8..37cc2281c42be 100644 --- a/api_docs/kbn_core_status_server_internal.mdx +++ b/api_docs/kbn_core_status_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-internal title: "@kbn/core-status-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-internal'] --- import kbnCoreStatusServerInternalObj from './kbn_core_status_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_mocks.mdx b/api_docs/kbn_core_status_server_mocks.mdx index 08e898d63952d..baf2f79e04953 100644 --- a/api_docs/kbn_core_status_server_mocks.mdx +++ b/api_docs/kbn_core_status_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-mocks title: "@kbn/core-status-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-mocks'] --- import kbnCoreStatusServerMocksObj from './kbn_core_status_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx index fb9cfe5e06eda..92205214d635c 100644 --- a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-deprecations-getters title: "@kbn/core-test-helpers-deprecations-getters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-deprecations-getters plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-deprecations-getters'] --- import kbnCoreTestHelpersDeprecationsGettersObj from './kbn_core_test_helpers_deprecations_getters.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx index 29b267c229a22..beb474f595f4f 100644 --- a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx +++ b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-http-setup-browser title: "@kbn/core-test-helpers-http-setup-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-http-setup-browser plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-http-setup-browser'] --- import kbnCoreTestHelpersHttpSetupBrowserObj from './kbn_core_test_helpers_http_setup_browser.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_kbn_server.mdx b/api_docs/kbn_core_test_helpers_kbn_server.mdx index 6835fa78db714..b6e914193798e 100644 --- a/api_docs/kbn_core_test_helpers_kbn_server.mdx +++ b/api_docs/kbn_core_test_helpers_kbn_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-kbn-server title: "@kbn/core-test-helpers-kbn-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-kbn-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-kbn-server'] --- import kbnCoreTestHelpersKbnServerObj from './kbn_core_test_helpers_kbn_server.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx index cec382dd67f99..7dbe9a90426e3 100644 --- a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx +++ b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-so-type-serializer title: "@kbn/core-test-helpers-so-type-serializer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-so-type-serializer plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-so-type-serializer'] --- import kbnCoreTestHelpersSoTypeSerializerObj from './kbn_core_test_helpers_so_type_serializer.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_test_utils.mdx b/api_docs/kbn_core_test_helpers_test_utils.mdx index 2ef04f8382050..0d6ecf679c5b0 100644 --- a/api_docs/kbn_core_test_helpers_test_utils.mdx +++ b/api_docs/kbn_core_test_helpers_test_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-test-utils title: "@kbn/core-test-helpers-test-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-test-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-test-utils'] --- import kbnCoreTestHelpersTestUtilsObj from './kbn_core_test_helpers_test_utils.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index f7c4977627024..0e6f51e1c3309 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser'] --- import kbnCoreThemeBrowserObj from './kbn_core_theme_browser.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_internal.mdx b/api_docs/kbn_core_theme_browser_internal.mdx index c4f34300dd40a..142f6035898c2 100644 --- a/api_docs/kbn_core_theme_browser_internal.mdx +++ b/api_docs/kbn_core_theme_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-internal title: "@kbn/core-theme-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-internal'] --- import kbnCoreThemeBrowserInternalObj from './kbn_core_theme_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index b06fc8d98390a..f7d9767b813e1 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-mocks'] --- import kbnCoreThemeBrowserMocksObj from './kbn_core_theme_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser.mdx b/api_docs/kbn_core_ui_settings_browser.mdx index 92824665b9051..e39273566fa65 100644 --- a/api_docs/kbn_core_ui_settings_browser.mdx +++ b/api_docs/kbn_core_ui_settings_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser title: "@kbn/core-ui-settings-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser'] --- import kbnCoreUiSettingsBrowserObj from './kbn_core_ui_settings_browser.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_internal.mdx b/api_docs/kbn_core_ui_settings_browser_internal.mdx index e36154decf480..5057dd29a20de 100644 --- a/api_docs/kbn_core_ui_settings_browser_internal.mdx +++ b/api_docs/kbn_core_ui_settings_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-internal title: "@kbn/core-ui-settings-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-internal'] --- import kbnCoreUiSettingsBrowserInternalObj from './kbn_core_ui_settings_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_mocks.mdx b/api_docs/kbn_core_ui_settings_browser_mocks.mdx index f3f0d04629090..ee79b6d1f903b 100644 --- a/api_docs/kbn_core_ui_settings_browser_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-mocks title: "@kbn/core-ui-settings-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-mocks'] --- import kbnCoreUiSettingsBrowserMocksObj from './kbn_core_ui_settings_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_common.mdx b/api_docs/kbn_core_ui_settings_common.mdx index 1c86bc3581739..f9b62b1dc92ce 100644 --- a/api_docs/kbn_core_ui_settings_common.mdx +++ b/api_docs/kbn_core_ui_settings_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-common title: "@kbn/core-ui-settings-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-common plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-common'] --- import kbnCoreUiSettingsCommonObj from './kbn_core_ui_settings_common.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server.mdx b/api_docs/kbn_core_ui_settings_server.mdx index d1191aae7a3e6..bec01eeeb7033 100644 --- a/api_docs/kbn_core_ui_settings_server.mdx +++ b/api_docs/kbn_core_ui_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server title: "@kbn/core-ui-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server'] --- import kbnCoreUiSettingsServerObj from './kbn_core_ui_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_internal.mdx b/api_docs/kbn_core_ui_settings_server_internal.mdx index 1527eee1a5ab3..cffe5df4a856a 100644 --- a/api_docs/kbn_core_ui_settings_server_internal.mdx +++ b/api_docs/kbn_core_ui_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-internal title: "@kbn/core-ui-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-internal'] --- import kbnCoreUiSettingsServerInternalObj from './kbn_core_ui_settings_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_mocks.mdx b/api_docs/kbn_core_ui_settings_server_mocks.mdx index 197fbf81d7eb7..9bb9dc64916f2 100644 --- a/api_docs/kbn_core_ui_settings_server_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-mocks title: "@kbn/core-ui-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-mocks'] --- import kbnCoreUiSettingsServerMocksObj from './kbn_core_ui_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server.mdx b/api_docs/kbn_core_usage_data_server.mdx index b8ebf1dc853dc..b5e3493f82751 100644 --- a/api_docs/kbn_core_usage_data_server.mdx +++ b/api_docs/kbn_core_usage_data_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server title: "@kbn/core-usage-data-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server'] --- import kbnCoreUsageDataServerObj from './kbn_core_usage_data_server.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_internal.mdx b/api_docs/kbn_core_usage_data_server_internal.mdx index 29af37cbe1019..0ca51dd533610 100644 --- a/api_docs/kbn_core_usage_data_server_internal.mdx +++ b/api_docs/kbn_core_usage_data_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-internal title: "@kbn/core-usage-data-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-internal'] --- import kbnCoreUsageDataServerInternalObj from './kbn_core_usage_data_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_mocks.mdx b/api_docs/kbn_core_usage_data_server_mocks.mdx index 7f08c46084950..d8aee8bf1a7f9 100644 --- a/api_docs/kbn_core_usage_data_server_mocks.mdx +++ b/api_docs/kbn_core_usage_data_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-mocks title: "@kbn/core-usage-data-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-mocks'] --- import kbnCoreUsageDataServerMocksObj from './kbn_core_usage_data_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server.mdx b/api_docs/kbn_core_user_settings_server.mdx index 5b45a9ecf9ee5..39a86e004ef48 100644 --- a/api_docs/kbn_core_user_settings_server.mdx +++ b/api_docs/kbn_core_user_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server title: "@kbn/core-user-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server'] --- import kbnCoreUserSettingsServerObj from './kbn_core_user_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server_internal.mdx b/api_docs/kbn_core_user_settings_server_internal.mdx index 5f97644173576..f851a352f922d 100644 --- a/api_docs/kbn_core_user_settings_server_internal.mdx +++ b/api_docs/kbn_core_user_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-internal title: "@kbn/core-user-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-internal plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server-internal'] --- import kbnCoreUserSettingsServerInternalObj from './kbn_core_user_settings_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server_mocks.mdx b/api_docs/kbn_core_user_settings_server_mocks.mdx index 050770ee7f4bf..81e97a47315f8 100644 --- a/api_docs/kbn_core_user_settings_server_mocks.mdx +++ b/api_docs/kbn_core_user_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-mocks title: "@kbn/core-user-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server-mocks'] --- import kbnCoreUserSettingsServerMocksObj from './kbn_core_user_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx index 91ac99d537313..83d77c684910f 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto'] --- import kbnCryptoObj from './kbn_crypto.devdocs.json'; diff --git a/api_docs/kbn_crypto_browser.mdx b/api_docs/kbn_crypto_browser.mdx index c7c180037c65a..e39edeea0c06c 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto-browser plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_cypress_config.mdx b/api_docs/kbn_cypress_config.mdx index e6e7c8e90d2b6..5bb1ef01346fe 100644 --- a/api_docs/kbn_cypress_config.mdx +++ b/api_docs/kbn_cypress_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cypress-config title: "@kbn/cypress-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cypress-config plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cypress-config'] --- import kbnCypressConfigObj from './kbn_cypress_config.devdocs.json'; diff --git a/api_docs/kbn_data_service.mdx b/api_docs/kbn_data_service.mdx index 08c01439606dc..7d2b146ae217b 100644 --- a/api_docs/kbn_data_service.mdx +++ b/api_docs/kbn_data_service.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-service title: "@kbn/data-service" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-service plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-service'] --- import kbnDataServiceObj from './kbn_data_service.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index ab7a457ca2973..e39f50ec0a177 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/datemath plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_analytics.mdx b/api_docs/kbn_deeplinks_analytics.mdx index bc29e3db1a242..8e46553951fd4 100644 --- a/api_docs/kbn_deeplinks_analytics.mdx +++ b/api_docs/kbn_deeplinks_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-analytics title: "@kbn/deeplinks-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-analytics plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-analytics'] --- import kbnDeeplinksAnalyticsObj from './kbn_deeplinks_analytics.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_devtools.mdx b/api_docs/kbn_deeplinks_devtools.mdx index 746d744ec21e6..b12bd06725302 100644 --- a/api_docs/kbn_deeplinks_devtools.mdx +++ b/api_docs/kbn_deeplinks_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-devtools title: "@kbn/deeplinks-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-devtools plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-devtools'] --- import kbnDeeplinksDevtoolsObj from './kbn_deeplinks_devtools.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_management.mdx b/api_docs/kbn_deeplinks_management.mdx index 0805671dc905c..d8de57ea9eb71 100644 --- a/api_docs/kbn_deeplinks_management.mdx +++ b/api_docs/kbn_deeplinks_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-management title: "@kbn/deeplinks-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-management plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-management'] --- import kbnDeeplinksManagementObj from './kbn_deeplinks_management.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_ml.mdx b/api_docs/kbn_deeplinks_ml.mdx index ef8961d35cec1..3970bdf914e05 100644 --- a/api_docs/kbn_deeplinks_ml.mdx +++ b/api_docs/kbn_deeplinks_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-ml title: "@kbn/deeplinks-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-ml plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-ml'] --- import kbnDeeplinksMlObj from './kbn_deeplinks_ml.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_observability.mdx b/api_docs/kbn_deeplinks_observability.mdx index 5bb85866f3529..24d30e69b89e7 100644 --- a/api_docs/kbn_deeplinks_observability.mdx +++ b/api_docs/kbn_deeplinks_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-observability title: "@kbn/deeplinks-observability" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-observability plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-observability'] --- import kbnDeeplinksObservabilityObj from './kbn_deeplinks_observability.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_search.mdx b/api_docs/kbn_deeplinks_search.mdx index aa6ad4b0817db..5ee03ee5870e7 100644 --- a/api_docs/kbn_deeplinks_search.mdx +++ b/api_docs/kbn_deeplinks_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-search title: "@kbn/deeplinks-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-search plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-search'] --- import kbnDeeplinksSearchObj from './kbn_deeplinks_search.devdocs.json'; diff --git a/api_docs/kbn_default_nav_analytics.mdx b/api_docs/kbn_default_nav_analytics.mdx index 0fe33103acb2e..d5b977c5cdee8 100644 --- a/api_docs/kbn_default_nav_analytics.mdx +++ b/api_docs/kbn_default_nav_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-analytics title: "@kbn/default-nav-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-analytics plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-analytics'] --- import kbnDefaultNavAnalyticsObj from './kbn_default_nav_analytics.devdocs.json'; diff --git a/api_docs/kbn_default_nav_devtools.mdx b/api_docs/kbn_default_nav_devtools.mdx index e9e9a3976b1b4..b9c31f0d9e983 100644 --- a/api_docs/kbn_default_nav_devtools.mdx +++ b/api_docs/kbn_default_nav_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-devtools title: "@kbn/default-nav-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-devtools plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-devtools'] --- import kbnDefaultNavDevtoolsObj from './kbn_default_nav_devtools.devdocs.json'; diff --git a/api_docs/kbn_default_nav_management.mdx b/api_docs/kbn_default_nav_management.mdx index 06c7cd0227d3c..7a6ec8b0b2418 100644 --- a/api_docs/kbn_default_nav_management.mdx +++ b/api_docs/kbn_default_nav_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-management title: "@kbn/default-nav-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-management plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-management'] --- import kbnDefaultNavManagementObj from './kbn_default_nav_management.devdocs.json'; diff --git a/api_docs/kbn_default_nav_ml.devdocs.json b/api_docs/kbn_default_nav_ml.devdocs.json index 45e7eed780000..ef16da2dd9b34 100644 --- a/api_docs/kbn_default_nav_ml.devdocs.json +++ b/api_docs/kbn_default_nav_ml.devdocs.json @@ -175,7 +175,7 @@ "label": "children", "description": [], "signature": [ - "[{ title: string; id: \"root\"; children: [{ link: \"ml:overview\"; }, { link: \"ml:notifications\"; }]; }, { title: any; id: \"anomaly_detection\"; children: [{ title: any; link: \"ml:anomalyDetection\"; }, { link: \"ml:anomalyExplorer\"; }, { link: \"ml:singleMetricViewer\"; }, { link: \"ml:settings\"; }]; }, { id: \"data_frame_analytics\"; title: any; children: [{ title: string; link: \"ml:dataFrameAnalytics\"; }, { link: \"ml:resultExplorer\"; }, { link: \"ml:analyticsMap\"; }]; }, { id: \"model_management\"; title: any; children: [{ link: \"ml:nodesOverview\"; }, { link: \"ml:nodes\"; }]; }, { id: \"data_visualizer\"; title: any; children: [{ title: any; link: \"ml:fileUpload\"; }, { title: any; link: \"ml:indexDataVisualizer\"; }]; }, { id: \"aiops_labs\"; title: any; children: [{ title: any; link: \"ml:explainLogRateSpikes\"; }, { link: \"ml:logPatternAnalysis\"; }]; }]" + "[{ title: string; id: \"root\"; children: [{ link: \"ml:overview\"; }, { link: \"ml:notifications\"; }]; }, { title: any; id: \"anomaly_detection\"; children: [{ title: any; link: \"ml:anomalyDetection\"; }, { link: \"ml:anomalyExplorer\"; }, { link: \"ml:singleMetricViewer\"; }, { link: \"ml:settings\"; }]; }, { id: \"data_frame_analytics\"; title: any; children: [{ title: string; link: \"ml:dataFrameAnalytics\"; }, { link: \"ml:resultExplorer\"; }, { link: \"ml:analyticsMap\"; }]; }, { id: \"model_management\"; title: any; children: [{ link: \"ml:nodesOverview\"; }, { link: \"ml:nodes\"; }]; }, { id: \"data_visualizer\"; title: any; children: [{ title: any; link: \"ml:fileUpload\"; }, { title: any; link: \"ml:indexDataVisualizer\"; }]; }, { id: \"aiops_labs\"; title: any; children: [{ link: \"ml:explainLogRateSpikes\"; }, { link: \"ml:logPatternAnalysis\"; }, { link: \"ml:changePointDetections\"; }]; }]" ], "path": "packages/default-nav/ml/default_navigation.ts", "deprecated": false, diff --git a/api_docs/kbn_default_nav_ml.mdx b/api_docs/kbn_default_nav_ml.mdx index 1e0c9b2fbf3a5..2a63690175ac1 100644 --- a/api_docs/kbn_default_nav_ml.mdx +++ b/api_docs/kbn_default_nav_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-ml title: "@kbn/default-nav-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-ml plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-ml'] --- import kbnDefaultNavMlObj from './kbn_default_nav_ml.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index 2c35736b115b5..0c5e55f1530a3 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-errors plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-errors'] --- import kbnDevCliErrorsObj from './kbn_dev_cli_errors.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_runner.mdx b/api_docs/kbn_dev_cli_runner.mdx index 2c92f09a74ba5..bc8cfc1ec87ad 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-runner plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-runner'] --- import kbnDevCliRunnerObj from './kbn_dev_cli_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_proc_runner.mdx b/api_docs/kbn_dev_proc_runner.mdx index 03f904c56b50f..2ccea747389a6 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-proc-runner plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-proc-runner'] --- import kbnDevProcRunnerObj from './kbn_dev_proc_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_utils.mdx b/api_docs/kbn_dev_utils.mdx index 976f85187ad6f..c57740608bdd1 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.devdocs.json b/api_docs/kbn_doc_links.devdocs.json index 38e1fa294e145..85fbfcea2892d 100644 --- a/api_docs/kbn_doc_links.devdocs.json +++ b/api_docs/kbn_doc_links.devdocs.json @@ -479,7 +479,7 @@ "label": "kibana", "description": [], "signature": [ - "{ readonly guide: string; readonly autocompleteSuggestions: string; readonly secureSavedObject: string; readonly xpackSecurity: string; }" + "{ readonly askElastic: string; readonly createGithubIssue: string; readonly feedback: string; readonly guide: string; readonly autocompleteSuggestions: string; readonly secureSavedObject: string; readonly xpackSecurity: string; }" ], "path": "packages/kbn-doc-links/src/types.ts", "deprecated": false, @@ -1008,6 +1008,17 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "@kbn/doc-links", + "id": "def-common.DocLinksMeta.elasticGithubUrl", + "type": "string", + "tags": [], + "label": "elasticGithubUrl", + "description": [], + "path": "packages/kbn-doc-links/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/doc-links", "id": "def-common.DocLinksMeta.docsWebsiteUrl", diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index db9b6d0b85d0b..b5e4d8524be67 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/doc-links plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/doc-links'] --- import kbnDocLinksObj from './kbn_doc_links.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/docs](https://github.com/orgs/elastic/teams/docs) for question | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 71 | 0 | 71 | 2 | +| 72 | 0 | 72 | 2 | ## Common diff --git a/api_docs/kbn_docs_utils.mdx b/api_docs/kbn_docs_utils.mdx index f911e310903cc..73419b45a76af 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/docs-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_dom_drag_drop.mdx b/api_docs/kbn_dom_drag_drop.mdx index e01a14c6c1ec2..4b6d0775c4b0e 100644 --- a/api_docs/kbn_dom_drag_drop.mdx +++ b/api_docs/kbn_dom_drag_drop.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dom-drag-drop title: "@kbn/dom-drag-drop" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dom-drag-drop plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dom-drag-drop'] --- import kbnDomDragDropObj from './kbn_dom_drag_drop.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index aa6acb99d617a..2eb3f2d9143d3 100644 --- a/api_docs/kbn_ebt_tools.mdx +++ b/api_docs/kbn_ebt_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ebt-tools title: "@kbn/ebt-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ebt-tools plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_ecs.mdx b/api_docs/kbn_ecs.mdx index 635d7c5314ee4..c7a1891389411 100644 --- a/api_docs/kbn_ecs.mdx +++ b/api_docs/kbn_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs title: "@kbn/ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs'] --- import kbnEcsObj from './kbn_ecs.devdocs.json'; diff --git a/api_docs/kbn_ecs_data_quality_dashboard.mdx b/api_docs/kbn_ecs_data_quality_dashboard.mdx index 69a8ef8bcb216..6a512056beced 100644 --- a/api_docs/kbn_ecs_data_quality_dashboard.mdx +++ b/api_docs/kbn_ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs-data-quality-dashboard title: "@kbn/ecs-data-quality-dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs-data-quality-dashboard plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs-data-quality-dashboard'] --- import kbnEcsDataQualityDashboardObj from './kbn_ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant.mdx b/api_docs/kbn_elastic_assistant.mdx index 76d40eae7cbe4..2f2101d15abd1 100644 --- a/api_docs/kbn_elastic_assistant.mdx +++ b/api_docs/kbn_elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant title: "@kbn/elastic-assistant" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant'] --- import kbnElasticAssistantObj from './kbn_elastic_assistant.devdocs.json'; diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index 8f5a641fc5a37..1239f4f1c5e00 100644 --- a/api_docs/kbn_es.mdx +++ b/api_docs/kbn_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es title: "@kbn/es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es'] --- import kbnEsObj from './kbn_es.devdocs.json'; diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index 62b55c2cbe6ea..37873c856d57f 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-archiver plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-archiver'] --- import kbnEsArchiverObj from './kbn_es_archiver.devdocs.json'; diff --git a/api_docs/kbn_es_errors.mdx b/api_docs/kbn_es_errors.mdx index b542ac4590921..78131a2926647 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-errors plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index 2dc931afb9ac6..6c30bc36e3c11 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-query plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] --- import kbnEsQueryObj from './kbn_es_query.devdocs.json'; diff --git a/api_docs/kbn_es_types.mdx b/api_docs/kbn_es_types.mdx index a688291dcb466..596300c1b5913 100644 --- a/api_docs/kbn_es_types.mdx +++ b/api_docs/kbn_es_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-types title: "@kbn/es-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-types plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-types'] --- import kbnEsTypesObj from './kbn_es_types.devdocs.json'; diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index 2ac3fbec286f1..570b75b924064 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/eslint-plugin-imports plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_expandable_flyout.mdx b/api_docs/kbn_expandable_flyout.mdx index 817316ee50a29..6cbe0e1c4e47c 100644 --- a/api_docs/kbn_expandable_flyout.mdx +++ b/api_docs/kbn_expandable_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-expandable-flyout title: "@kbn/expandable-flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/expandable-flyout plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/expandable-flyout'] --- import kbnExpandableFlyoutObj from './kbn_expandable_flyout.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index 9db7ac2995823..dca9f7d611e9c 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-types plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index 5e918d373f28e..762218ef04c43 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/find-used-node-modules plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] --- import kbnFindUsedNodeModulesObj from './kbn_find_used_node_modules.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index 08df7c49d423f..ee52ceb039d68 100644 --- a/api_docs/kbn_ftr_common_functional_services.mdx +++ b/api_docs/kbn_ftr_common_functional_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-services title: "@kbn/ftr-common-functional-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-services plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-services'] --- import kbnFtrCommonFunctionalServicesObj from './kbn_ftr_common_functional_services.devdocs.json'; diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index 5db131ac2aa64..614a3fc288bb2 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_generate_console_definitions.mdx b/api_docs/kbn_generate_console_definitions.mdx index 134885dce4e31..f5948cfc5773d 100644 --- a/api_docs/kbn_generate_console_definitions.mdx +++ b/api_docs/kbn_generate_console_definitions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-console-definitions title: "@kbn/generate-console-definitions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-console-definitions plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-console-definitions'] --- import kbnGenerateConsoleDefinitionsObj from './kbn_generate_console_definitions.devdocs.json'; diff --git a/api_docs/kbn_generate_csv.mdx b/api_docs/kbn_generate_csv.mdx index 29277fd436268..67f03a9e45bc8 100644 --- a/api_docs/kbn_generate_csv.mdx +++ b/api_docs/kbn_generate_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv title: "@kbn/generate-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv'] --- import kbnGenerateCsvObj from './kbn_generate_csv.devdocs.json'; diff --git a/api_docs/kbn_generate_csv_types.mdx b/api_docs/kbn_generate_csv_types.mdx index 08cb04241e202..797b415e2da5f 100644 --- a/api_docs/kbn_generate_csv_types.mdx +++ b/api_docs/kbn_generate_csv_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv-types title: "@kbn/generate-csv-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv-types plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv-types'] --- import kbnGenerateCsvTypesObj from './kbn_generate_csv_types.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index a0b070753b4af..0d95afc4efd7f 100644 --- a/api_docs/kbn_guided_onboarding.mdx +++ b/api_docs/kbn_guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-guided-onboarding title: "@kbn/guided-onboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/guided-onboarding plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/guided-onboarding'] --- import kbnGuidedOnboardingObj from './kbn_guided_onboarding.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index 6053a1722a462..60231ea44d306 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/handlebars plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/handlebars'] --- import kbnHandlebarsObj from './kbn_handlebars.devdocs.json'; diff --git a/api_docs/kbn_hapi_mocks.mdx b/api_docs/kbn_hapi_mocks.mdx index 46d6377ce2fd5..71a44612532b0 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/hapi-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] --- import kbnHapiMocksObj from './kbn_hapi_mocks.devdocs.json'; diff --git a/api_docs/kbn_health_gateway_server.mdx b/api_docs/kbn_health_gateway_server.mdx index 8af183c664100..a19ad26316f30 100644 --- a/api_docs/kbn_health_gateway_server.mdx +++ b/api_docs/kbn_health_gateway_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-health-gateway-server title: "@kbn/health-gateway-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/health-gateway-server plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/health-gateway-server'] --- import kbnHealthGatewayServerObj from './kbn_health_gateway_server.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index 3da9b96032b86..a569f10bf7bd9 100644 --- a/api_docs/kbn_home_sample_data_card.mdx +++ b/api_docs/kbn_home_sample_data_card.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-card title: "@kbn/home-sample-data-card" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-card plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-card'] --- import kbnHomeSampleDataCardObj from './kbn_home_sample_data_card.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_tab.mdx b/api_docs/kbn_home_sample_data_tab.mdx index 97225879065a0..6daa232d8b9b0 100644 --- a/api_docs/kbn_home_sample_data_tab.mdx +++ b/api_docs/kbn_home_sample_data_tab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-tab title: "@kbn/home-sample-data-tab" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-tab plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-tab'] --- import kbnHomeSampleDataTabObj from './kbn_home_sample_data_tab.devdocs.json'; diff --git a/api_docs/kbn_i18n.mdx b/api_docs/kbn_i18n.mdx index 6d95092b2fb2a..347792b5678fa 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] --- import kbnI18nObj from './kbn_i18n.devdocs.json'; diff --git a/api_docs/kbn_i18n_react.mdx b/api_docs/kbn_i18n_react.mdx index 34e812c2a9724..bb21d9c01981e 100644 --- a/api_docs/kbn_i18n_react.mdx +++ b/api_docs/kbn_i18n_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n-react title: "@kbn/i18n-react" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n-react plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n-react'] --- import kbnI18nReactObj from './kbn_i18n_react.devdocs.json'; diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index 08c68565ae2b1..65ff9f5173629 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/import-resolver plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_infra_forge.mdx b/api_docs/kbn_infra_forge.mdx index 0a01ba4056433..a7c683a49990d 100644 --- a/api_docs/kbn_infra_forge.mdx +++ b/api_docs/kbn_infra_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-infra-forge title: "@kbn/infra-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/infra-forge plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/infra-forge'] --- import kbnInfraForgeObj from './kbn_infra_forge.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index 818fce8ad7e93..77cd989c02842 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/interpreter plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index 0d332dec34ec3..d2fa70c64ec43 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/io-ts-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index 8161ced3eb36b..38fdd3c2e61d6 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/jest-serializers plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/jest-serializers'] --- import kbnJestSerializersObj from './kbn_jest_serializers.devdocs.json'; diff --git a/api_docs/kbn_journeys.mdx b/api_docs/kbn_journeys.mdx index cf2f98e7566f3..d687e871dc698 100644 --- a/api_docs/kbn_journeys.mdx +++ b/api_docs/kbn_journeys.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-journeys title: "@kbn/journeys" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/journeys plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/journeys'] --- import kbnJourneysObj from './kbn_journeys.devdocs.json'; diff --git a/api_docs/kbn_json_ast.mdx b/api_docs/kbn_json_ast.mdx index 6116993499422..e2c176c38744e 100644 --- a/api_docs/kbn_json_ast.mdx +++ b/api_docs/kbn_json_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-ast title: "@kbn/json-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-ast plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-ast'] --- import kbnJsonAstObj from './kbn_json_ast.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index 0af2869630512..b8bd537cdf4b1 100644 --- a/api_docs/kbn_kibana_manifest_schema.mdx +++ b/api_docs/kbn_kibana_manifest_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-schema title: "@kbn/kibana-manifest-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-schema plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; diff --git a/api_docs/kbn_language_documentation_popover.mdx b/api_docs/kbn_language_documentation_popover.mdx index f2261e06cb1e2..eef98288dea02 100644 --- a/api_docs/kbn_language_documentation_popover.mdx +++ b/api_docs/kbn_language_documentation_popover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-language-documentation-popover title: "@kbn/language-documentation-popover" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/language-documentation-popover plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/language-documentation-popover'] --- import kbnLanguageDocumentationPopoverObj from './kbn_language_documentation_popover.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index 337cf5640c3ae..150f581c08744 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging'] --- import kbnLoggingObj from './kbn_logging.devdocs.json'; diff --git a/api_docs/kbn_logging_mocks.mdx b/api_docs/kbn_logging_mocks.mdx index ed7a25e96af4d..f9231eb985b6c 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index 27686b46252de..f035ae091252d 100644 --- a/api_docs/kbn_managed_vscode_config.mdx +++ b/api_docs/kbn_managed_vscode_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-vscode-config title: "@kbn/managed-vscode-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-vscode-config plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_management_cards_navigation.devdocs.json b/api_docs/kbn_management_cards_navigation.devdocs.json new file mode 100644 index 0000000000000..35116d25824c5 --- /dev/null +++ b/api_docs/kbn_management_cards_navigation.devdocs.json @@ -0,0 +1,191 @@ +{ + "id": "@kbn/management-cards-navigation", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/management-cards-navigation", + "id": "def-common.CardsNavigation", + "type": "Function", + "tags": [], + "label": "CardsNavigation", + "description": [], + "signature": [ + "({ sections, appBasePath, onCardClick, hideLinksTo, }: ", + { + "pluginId": "@kbn/management-cards-navigation", + "scope": "common", + "docId": "kibKbnManagementCardsNavigationPluginApi", + "section": "def-common.CardsNavigationComponentProps", + "text": "CardsNavigationComponentProps" + }, + ") => JSX.Element" + ], + "path": "packages/kbn-management/cards_navigation/src/cards_navigation.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/management-cards-navigation", + "id": "def-common.CardsNavigation.$1", + "type": "Object", + "tags": [], + "label": "{\n sections,\n appBasePath,\n onCardClick,\n hideLinksTo = [],\n}", + "description": [], + "signature": [ + { + "pluginId": "@kbn/management-cards-navigation", + "scope": "common", + "docId": "kibKbnManagementCardsNavigationPluginApi", + "section": "def-common.CardsNavigationComponentProps", + "text": "CardsNavigationComponentProps" + } + ], + "path": "packages/kbn-management/cards_navigation/src/cards_navigation.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "@kbn/management-cards-navigation", + "id": "def-common.CardsNavigationComponentProps", + "type": "Interface", + "tags": [], + "label": "CardsNavigationComponentProps", + "description": [], + "path": "packages/kbn-management/cards_navigation/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/management-cards-navigation", + "id": "def-common.CardsNavigationComponentProps.sections", + "type": "Array", + "tags": [], + "label": "sections", + "description": [], + "signature": [ + "AppRegistrySections", + "[]" + ], + "path": "packages/kbn-management/cards_navigation/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/management-cards-navigation", + "id": "def-common.CardsNavigationComponentProps.appBasePath", + "type": "string", + "tags": [], + "label": "appBasePath", + "description": [], + "path": "packages/kbn-management/cards_navigation/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/management-cards-navigation", + "id": "def-common.CardsNavigationComponentProps.onCardClick", + "type": "Function", + "tags": [], + "label": "onCardClick", + "description": [], + "signature": [ + "((e: React.MouseEvent) => void) | undefined" + ], + "path": "packages/kbn-management/cards_navigation/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/management-cards-navigation", + "id": "def-common.CardsNavigationComponentProps.onCardClick.$1", + "type": "Object", + "tags": [], + "label": "e", + "description": [], + "signature": [ + "React.MouseEvent" + ], + "path": "packages/kbn-management/cards_navigation/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/management-cards-navigation", + "id": "def-common.CardsNavigationComponentProps.hideLinksTo", + "type": "Array", + "tags": [], + "label": "hideLinksTo", + "description": [], + "signature": [ + "(\"transform\" | \"tags\" | \"maintenanceWindows\" | \"dataViews\" | \"data_view\" | \"filesManagement\" | \"reporting\" | \"api_keys\" | \"index_management\" | \"ingest_pipelines\" | \"jobsListLink\" | \"objects\" | \"pipelines\" | \"triggersActions\" | \"triggersActionsConnectors\")[] | undefined" + ], + "path": "packages/kbn-management/cards_navigation/src/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [ + { + "parentPluginId": "@kbn/management-cards-navigation", + "id": "def-common.appIds", + "type": "Enum", + "tags": [], + "label": "appIds", + "description": [], + "path": "packages/kbn-management/cards_navigation/src/consts.tsx", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "misc": [ + { + "parentPluginId": "@kbn/management-cards-navigation", + "id": "def-common.AppId", + "type": "Type", + "tags": [], + "label": "AppId", + "description": [], + "signature": [ + "\"transform\" | \"tags\" | \"maintenanceWindows\" | \"dataViews\" | \"data_view\" | \"filesManagement\" | \"reporting\" | \"api_keys\" | \"index_management\" | \"ingest_pipelines\" | \"jobsListLink\" | \"objects\" | \"pipelines\" | \"triggersActions\" | \"triggersActionsConnectors\"" + ], + "path": "packages/kbn-management/cards_navigation/src/consts.tsx", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_management_cards_navigation.mdx b/api_docs/kbn_management_cards_navigation.mdx new file mode 100644 index 0000000000000..1031ad4ffaa31 --- /dev/null +++ b/api_docs/kbn_management_cards_navigation.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: kibKbnManagementCardsNavigationPluginApi +slug: /kibana-dev-docs/api/kbn-management-cards-navigation +title: "@kbn/management-cards-navigation" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/management-cards-navigation plugin +date: 2023-07-03 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-cards-navigation'] +--- +import kbnManagementCardsNavigationObj from './kbn_management_cards_navigation.devdocs.json'; + + + +Contact [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 10 | 0 | 10 | 1 | + +## Common + +### Functions + + +### Interfaces + + +### Enums + + +### Consts, variables and types + + diff --git a/api_docs/kbn_management_storybook_config.devdocs.json b/api_docs/kbn_management_storybook_config.devdocs.json new file mode 100644 index 0000000000000..7615017ad3902 --- /dev/null +++ b/api_docs/kbn_management_storybook_config.devdocs.json @@ -0,0 +1,62 @@ +{ + "id": "@kbn/management-storybook-config", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/management-storybook-config", + "id": "def-common.TITLE", + "type": "string", + "tags": [], + "label": "TITLE", + "description": [ + "The title of the Storybook." + ], + "signature": [ + "\"kbn-management storybook\"" + ], + "path": "packages/kbn-management/storybook/config/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/management-storybook-config", + "id": "def-common.URL", + "type": "string", + "tags": [], + "label": "URL", + "description": [ + "The remote URL of the root from which Storybook loads stories for kbn-management." + ], + "signature": [ + "\"https://github.com/elastic/kibana/tree/main/packages/kbn-management\"" + ], + "path": "packages/kbn-management/storybook/config/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_management_storybook_config.mdx b/api_docs/kbn_management_storybook_config.mdx new file mode 100644 index 0000000000000..6be3e936cde50 --- /dev/null +++ b/api_docs/kbn_management_storybook_config.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: kibKbnManagementStorybookConfigPluginApi +slug: /kibana-dev-docs/api/kbn-management-storybook-config +title: "@kbn/management-storybook-config" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/management-storybook-config plugin +date: 2023-07-03 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-storybook-config'] +--- +import kbnManagementStorybookConfigObj from './kbn_management_storybook_config.devdocs.json'; + + + +Contact [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 2 | 0 | 0 | 0 | + +## Common + +### Consts, variables and types + + diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index 5147299e37663..7c38ff1a3d33b 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mapbox-gl plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_maps_vector_tile_utils.mdx b/api_docs/kbn_maps_vector_tile_utils.mdx index 51b5793e598f9..4441d8cfe1704 100644 --- a/api_docs/kbn_maps_vector_tile_utils.mdx +++ b/api_docs/kbn_maps_vector_tile_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-maps-vector-tile-utils title: "@kbn/maps-vector-tile-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/maps-vector-tile-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/maps-vector-tile-utils'] --- import kbnMapsVectorTileUtilsObj from './kbn_maps_vector_tile_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index f7d230fc98359..257ba4e7fc4e5 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-agg-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-agg-utils'] --- import kbnMlAggUtilsObj from './kbn_ml_agg_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_anomaly_utils.mdx b/api_docs/kbn_ml_anomaly_utils.mdx index 79858ac6c6b98..bd1e1501481cd 100644 --- a/api_docs/kbn_ml_anomaly_utils.mdx +++ b/api_docs/kbn_ml_anomaly_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-anomaly-utils title: "@kbn/ml-anomaly-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-anomaly-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-anomaly-utils'] --- import kbnMlAnomalyUtilsObj from './kbn_ml_anomaly_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_data_frame_analytics_utils.mdx b/api_docs/kbn_ml_data_frame_analytics_utils.mdx index 404c6677a8d45..67992c1e25073 100644 --- a/api_docs/kbn_ml_data_frame_analytics_utils.mdx +++ b/api_docs/kbn_ml_data_frame_analytics_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-frame-analytics-utils title: "@kbn/ml-data-frame-analytics-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-frame-analytics-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-frame-analytics-utils'] --- import kbnMlDataFrameAnalyticsUtilsObj from './kbn_ml_data_frame_analytics_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_data_grid.mdx b/api_docs/kbn_ml_data_grid.mdx index 3cf0bd8fc0b5f..9639238308b58 100644 --- a/api_docs/kbn_ml_data_grid.mdx +++ b/api_docs/kbn_ml_data_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-grid title: "@kbn/ml-data-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-grid plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-grid'] --- import kbnMlDataGridObj from './kbn_ml_data_grid.devdocs.json'; diff --git a/api_docs/kbn_ml_date_picker.mdx b/api_docs/kbn_ml_date_picker.mdx index fcc436bd1a59e..8ae9d959e5c41 100644 --- a/api_docs/kbn_ml_date_picker.mdx +++ b/api_docs/kbn_ml_date_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-picker title: "@kbn/ml-date-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-picker plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-picker'] --- import kbnMlDatePickerObj from './kbn_ml_date_picker.devdocs.json'; diff --git a/api_docs/kbn_ml_date_utils.mdx b/api_docs/kbn_ml_date_utils.mdx index ceb975a261cc3..d6a6621ffa99c 100644 --- a/api_docs/kbn_ml_date_utils.mdx +++ b/api_docs/kbn_ml_date_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-utils title: "@kbn/ml-date-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-utils'] --- import kbnMlDateUtilsObj from './kbn_ml_date_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_error_utils.mdx b/api_docs/kbn_ml_error_utils.mdx index 4183652a9393a..f73cb8fb9678f 100644 --- a/api_docs/kbn_ml_error_utils.mdx +++ b/api_docs/kbn_ml_error_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-error-utils title: "@kbn/ml-error-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-error-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-error-utils'] --- import kbnMlErrorUtilsObj from './kbn_ml_error_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_is_defined.mdx b/api_docs/kbn_ml_is_defined.mdx index 58ba1539b3081..4a3a0ec674c29 100644 --- a/api_docs/kbn_ml_is_defined.mdx +++ b/api_docs/kbn_ml_is_defined.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-defined title: "@kbn/ml-is-defined" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-defined plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-defined'] --- import kbnMlIsDefinedObj from './kbn_ml_is_defined.devdocs.json'; diff --git a/api_docs/kbn_ml_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index 2d2f89cdd9335..6a39f28206866 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-populated-object plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-populated-object'] --- import kbnMlIsPopulatedObjectObj from './kbn_ml_is_populated_object.devdocs.json'; diff --git a/api_docs/kbn_ml_kibana_theme.mdx b/api_docs/kbn_ml_kibana_theme.mdx index 850f72a2d8a25..e074cff2362f1 100644 --- a/api_docs/kbn_ml_kibana_theme.mdx +++ b/api_docs/kbn_ml_kibana_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-kibana-theme title: "@kbn/ml-kibana-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-kibana-theme plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-kibana-theme'] --- import kbnMlKibanaThemeObj from './kbn_ml_kibana_theme.devdocs.json'; diff --git a/api_docs/kbn_ml_local_storage.mdx b/api_docs/kbn_ml_local_storage.mdx index 49e2245c6a7a5..c612dde0333f7 100644 --- a/api_docs/kbn_ml_local_storage.mdx +++ b/api_docs/kbn_ml_local_storage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-local-storage title: "@kbn/ml-local-storage" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-local-storage plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-local-storage'] --- import kbnMlLocalStorageObj from './kbn_ml_local_storage.devdocs.json'; diff --git a/api_docs/kbn_ml_nested_property.mdx b/api_docs/kbn_ml_nested_property.mdx index 3e9f27b990006..0588bf88fca63 100644 --- a/api_docs/kbn_ml_nested_property.mdx +++ b/api_docs/kbn_ml_nested_property.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-nested-property title: "@kbn/ml-nested-property" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-nested-property plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-nested-property'] --- import kbnMlNestedPropertyObj from './kbn_ml_nested_property.devdocs.json'; diff --git a/api_docs/kbn_ml_number_utils.mdx b/api_docs/kbn_ml_number_utils.mdx index c56e7b9337574..6efe0f92bf669 100644 --- a/api_docs/kbn_ml_number_utils.mdx +++ b/api_docs/kbn_ml_number_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-number-utils title: "@kbn/ml-number-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-number-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-number-utils'] --- import kbnMlNumberUtilsObj from './kbn_ml_number_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_query_utils.mdx b/api_docs/kbn_ml_query_utils.mdx index 48d8e0fbc1f91..4c9d14a51e53a 100644 --- a/api_docs/kbn_ml_query_utils.mdx +++ b/api_docs/kbn_ml_query_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-query-utils title: "@kbn/ml-query-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-query-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-query-utils'] --- import kbnMlQueryUtilsObj from './kbn_ml_query_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_random_sampler_utils.mdx b/api_docs/kbn_ml_random_sampler_utils.mdx index 8c5c2c7b50af1..1b34b0a238b16 100644 --- a/api_docs/kbn_ml_random_sampler_utils.mdx +++ b/api_docs/kbn_ml_random_sampler_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-random-sampler-utils title: "@kbn/ml-random-sampler-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-random-sampler-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-random-sampler-utils'] --- import kbnMlRandomSamplerUtilsObj from './kbn_ml_random_sampler_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_route_utils.mdx b/api_docs/kbn_ml_route_utils.mdx index e666f90f6b589..42d7a07ebc6d5 100644 --- a/api_docs/kbn_ml_route_utils.mdx +++ b/api_docs/kbn_ml_route_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-route-utils title: "@kbn/ml-route-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-route-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-route-utils'] --- import kbnMlRouteUtilsObj from './kbn_ml_route_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_runtime_field_utils.mdx b/api_docs/kbn_ml_runtime_field_utils.mdx index e12985330e7f7..a16555eb88815 100644 --- a/api_docs/kbn_ml_runtime_field_utils.mdx +++ b/api_docs/kbn_ml_runtime_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-runtime-field-utils title: "@kbn/ml-runtime-field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-runtime-field-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-runtime-field-utils'] --- import kbnMlRuntimeFieldUtilsObj from './kbn_ml_runtime_field_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index 81b53da1eeec3..22e457a4ebcf2 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-string-hash plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_ml_trained_models_utils.mdx b/api_docs/kbn_ml_trained_models_utils.mdx index 322b7df40f21c..2ec62c0170c42 100644 --- a/api_docs/kbn_ml_trained_models_utils.mdx +++ b/api_docs/kbn_ml_trained_models_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-trained-models-utils title: "@kbn/ml-trained-models-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-trained-models-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-trained-models-utils'] --- import kbnMlTrainedModelsUtilsObj from './kbn_ml_trained_models_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_url_state.mdx b/api_docs/kbn_ml_url_state.mdx index a0dee28b430bb..85b4f63b897b4 100644 --- a/api_docs/kbn_ml_url_state.mdx +++ b/api_docs/kbn_ml_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-url-state title: "@kbn/ml-url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-url-state plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index 3609f9c080732..f302e95a34e30 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/monaco plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_object_versioning.mdx b/api_docs/kbn_object_versioning.mdx index e5d61420d7f78..1d18012d3d06e 100644 --- a/api_docs/kbn_object_versioning.mdx +++ b/api_docs/kbn_object_versioning.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-object-versioning title: "@kbn/object-versioning" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/object-versioning plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/object-versioning'] --- import kbnObjectVersioningObj from './kbn_object_versioning.devdocs.json'; diff --git a/api_docs/kbn_observability_alert_details.mdx b/api_docs/kbn_observability_alert_details.mdx index 4299751793d51..ab0be9166607a 100644 --- a/api_docs/kbn_observability_alert_details.mdx +++ b/api_docs/kbn_observability_alert_details.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alert-details title: "@kbn/observability-alert-details" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alert-details plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alert-details'] --- import kbnObservabilityAlertDetailsObj from './kbn_observability_alert_details.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 7fd8398aa88f7..dcfab0f732e8d 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer'] --- import kbnOptimizerObj from './kbn_optimizer.devdocs.json'; diff --git a/api_docs/kbn_optimizer_webpack_helpers.mdx b/api_docs/kbn_optimizer_webpack_helpers.mdx index 23af54fccf240..8de49e90a1219 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer-webpack-helpers'] --- import kbnOptimizerWebpackHelpersObj from './kbn_optimizer_webpack_helpers.devdocs.json'; diff --git a/api_docs/kbn_osquery_io_ts_types.mdx b/api_docs/kbn_osquery_io_ts_types.mdx index 7cd16c465206b..e96708f0d7dee 100644 --- a/api_docs/kbn_osquery_io_ts_types.mdx +++ b/api_docs/kbn_osquery_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-osquery-io-ts-types title: "@kbn/osquery-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/osquery-io-ts-types plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/osquery-io-ts-types'] --- import kbnOsqueryIoTsTypesObj from './kbn_osquery_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index 4492c48928260..a698699823a0b 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/performance-testing-dataset-extractor'] --- import kbnPerformanceTestingDatasetExtractorObj from './kbn_performance_testing_dataset_extractor.devdocs.json'; diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index ff9a96c9e9bca..6d31f2d49b399 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-generator plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-generator'] --- import kbnPluginGeneratorObj from './kbn_plugin_generator.devdocs.json'; diff --git a/api_docs/kbn_plugin_helpers.mdx b/api_docs/kbn_plugin_helpers.mdx index b8368fa20e46a..8f18fcc306e78 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-helpers plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_random_sampling.mdx b/api_docs/kbn_random_sampling.mdx index cc67c47f1bc56..cb2c76074f3d9 100644 --- a/api_docs/kbn_random_sampling.mdx +++ b/api_docs/kbn_random_sampling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-random-sampling title: "@kbn/random-sampling" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/random-sampling plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/random-sampling'] --- import kbnRandomSamplingObj from './kbn_random_sampling.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index 9c530b7779f8b..fbc5385de83c6 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-field plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index a8f3d0a8052f9..b10707fad90a5 100644 --- a/api_docs/kbn_repo_file_maps.mdx +++ b/api_docs/kbn_repo_file_maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-file-maps title: "@kbn/repo-file-maps" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-file-maps plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-file-maps'] --- import kbnRepoFileMapsObj from './kbn_repo_file_maps.devdocs.json'; diff --git a/api_docs/kbn_repo_linter.mdx b/api_docs/kbn_repo_linter.mdx index bbae305f56b89..1a5fba09feb55 100644 --- a/api_docs/kbn_repo_linter.mdx +++ b/api_docs/kbn_repo_linter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-linter title: "@kbn/repo-linter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-linter plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-linter'] --- import kbnRepoLinterObj from './kbn_repo_linter.devdocs.json'; diff --git a/api_docs/kbn_repo_path.mdx b/api_docs/kbn_repo_path.mdx index ad629e2e80480..a9e4810644cab 100644 --- a/api_docs/kbn_repo_path.mdx +++ b/api_docs/kbn_repo_path.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-path title: "@kbn/repo-path" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-path plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-path'] --- import kbnRepoPathObj from './kbn_repo_path.devdocs.json'; diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index ee4a70da1444e..1a20fb5676183 100644 --- a/api_docs/kbn_repo_source_classifier.mdx +++ b/api_docs/kbn_repo_source_classifier.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-source-classifier title: "@kbn/repo-source-classifier" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-source-classifier plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_reporting_common.mdx b/api_docs/kbn_reporting_common.mdx index e627c153b06f5..d9b27d4ae9f9c 100644 --- a/api_docs/kbn_reporting_common.mdx +++ b/api_docs/kbn_reporting_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-common title: "@kbn/reporting-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-common plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-common'] --- import kbnReportingCommonObj from './kbn_reporting_common.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index 87a9a08671452..f4386f74487ed 100644 --- a/api_docs/kbn_rison.mdx +++ b/api_docs/kbn_rison.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rison title: "@kbn/rison" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rison plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_rrule.devdocs.json b/api_docs/kbn_rrule.devdocs.json new file mode 100644 index 0000000000000..849f4cc2f8ff7 --- /dev/null +++ b/api_docs/kbn_rrule.devdocs.json @@ -0,0 +1,273 @@ +{ + "id": "@kbn/rrule", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [ + { + "parentPluginId": "@kbn/rrule", + "id": "def-common.RRule", + "type": "Class", + "tags": [], + "label": "RRule", + "description": [], + "path": "packages/kbn-rrule/rrule.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/rrule", + "id": "def-common.RRule.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "packages/kbn-rrule/rrule.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/rrule", + "id": "def-common.RRule.Unnamed.$1", + "type": "CompoundType", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "Options" + ], + "path": "packages/kbn-rrule/rrule.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/rrule", + "id": "def-common.RRule.between", + "type": "Function", + "tags": [], + "label": "between", + "description": [], + "signature": [ + "(start: Date, end: Date) => Date[]" + ], + "path": "packages/kbn-rrule/rrule.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/rrule", + "id": "def-common.RRule.between.$1", + "type": "Object", + "tags": [], + "label": "start", + "description": [], + "signature": [ + "Date" + ], + "path": "packages/kbn-rrule/rrule.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/rrule", + "id": "def-common.RRule.between.$2", + "type": "Object", + "tags": [], + "label": "end", + "description": [], + "signature": [ + "Date" + ], + "path": "packages/kbn-rrule/rrule.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/rrule", + "id": "def-common.RRule.before", + "type": "Function", + "tags": [], + "label": "before", + "description": [], + "signature": [ + "(dt: Date) => Date" + ], + "path": "packages/kbn-rrule/rrule.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/rrule", + "id": "def-common.RRule.before.$1", + "type": "Object", + "tags": [], + "label": "dt", + "description": [], + "signature": [ + "Date" + ], + "path": "packages/kbn-rrule/rrule.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/rrule", + "id": "def-common.RRule.after", + "type": "Function", + "tags": [], + "label": "after", + "description": [], + "signature": [ + "(dt: Date) => Date | null" + ], + "path": "packages/kbn-rrule/rrule.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/rrule", + "id": "def-common.RRule.after.$1", + "type": "Object", + "tags": [], + "label": "dt", + "description": [], + "signature": [ + "Date" + ], + "path": "packages/kbn-rrule/rrule.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/rrule", + "id": "def-common.RRule.all", + "type": "Function", + "tags": [], + "label": "all", + "description": [], + "signature": [ + "(limit?: number) => AllResult" + ], + "path": "packages/kbn-rrule/rrule.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/rrule", + "id": "def-common.RRule.all.$1", + "type": "number", + "tags": [], + "label": "limit", + "description": [], + "signature": [ + "number" + ], + "path": "packages/kbn-rrule/rrule.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "functions": [], + "interfaces": [], + "enums": [ + { + "parentPluginId": "@kbn/rrule", + "id": "def-common.Frequency", + "type": "Enum", + "tags": [], + "label": "Frequency", + "description": [], + "path": "packages/kbn-rrule/rrule.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/rrule", + "id": "def-common.Weekday", + "type": "Enum", + "tags": [], + "label": "Weekday", + "description": [], + "path": "packages/kbn-rrule/rrule.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "misc": [ + { + "parentPluginId": "@kbn/rrule", + "id": "def-common.ConstructorOptions", + "type": "Type", + "tags": [], + "label": "ConstructorOptions", + "description": [], + "signature": [ + "Omit & { byweekday?: (string | number)[] | null | undefined; wkst?: number | \"MO\" | \"TU\" | \"WE\" | \"TH\" | \"FR\" | \"SA\" | \"SU\" | null | undefined; }" + ], + "path": "packages/kbn-rrule/rrule.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/rrule", + "id": "def-common.WeekdayStr", + "type": "Type", + "tags": [], + "label": "WeekdayStr", + "description": [], + "signature": [ + "\"MO\" | \"TU\" | \"WE\" | \"TH\" | \"FR\" | \"SA\" | \"SU\"" + ], + "path": "packages/kbn-rrule/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_rrule.mdx b/api_docs/kbn_rrule.mdx new file mode 100644 index 0000000000000..7fe59eb4f5553 --- /dev/null +++ b/api_docs/kbn_rrule.mdx @@ -0,0 +1,36 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnRrulePluginApi +slug: /kibana-dev-docs/api/kbn-rrule +title: "@kbn/rrule" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/rrule plugin +date: 2023-07-03 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rrule'] +--- +import kbnRruleObj from './kbn_rrule.devdocs.json'; + + + +Contact [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 16 | 0 | 16 | 1 | + +## Common + +### Classes + + +### Enums + + +### Consts, variables and types + + diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index 7a5fac77e05f9..6e4d33e4c9fd9 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rule-data-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_saved_objects_settings.mdx b/api_docs/kbn_saved_objects_settings.mdx index 02407a5984e2b..b259e39c97e00 100644 --- a/api_docs/kbn_saved_objects_settings.mdx +++ b/api_docs/kbn_saved_objects_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-saved-objects-settings title: "@kbn/saved-objects-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/saved-objects-settings plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/saved-objects-settings'] --- import kbnSavedObjectsSettingsObj from './kbn_saved_objects_settings.devdocs.json'; diff --git a/api_docs/kbn_security_solution_side_nav.mdx b/api_docs/kbn_security_solution_side_nav.mdx index 13c1d7da7e2f0..3c3c34c2c8d23 100644 --- a/api_docs/kbn_security_solution_side_nav.mdx +++ b/api_docs/kbn_security_solution_side_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-side-nav title: "@kbn/security-solution-side-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-side-nav plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-side-nav'] --- import kbnSecuritySolutionSideNavObj from './kbn_security_solution_side_nav.devdocs.json'; diff --git a/api_docs/kbn_security_solution_storybook_config.mdx b/api_docs/kbn_security_solution_storybook_config.mdx index 89fe6d2bd164f..ae75ed838a5fb 100644 --- a/api_docs/kbn_security_solution_storybook_config.mdx +++ b/api_docs/kbn_security_solution_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-storybook-config title: "@kbn/security-solution-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-storybook-config plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-storybook-config'] --- import kbnSecuritySolutionStorybookConfigObj from './kbn_security_solution_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 9c9daeef2a640..c469670905c4e 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_data_table.mdx b/api_docs/kbn_securitysolution_data_table.mdx index f32526c450d3d..239a7da01e2ba 100644 --- a/api_docs/kbn_securitysolution_data_table.mdx +++ b/api_docs/kbn_securitysolution_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-data-table title: "@kbn/securitysolution-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-data-table plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-data-table'] --- import kbnSecuritysolutionDataTableObj from './kbn_securitysolution_data_table.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_ecs.mdx b/api_docs/kbn_securitysolution_ecs.mdx index 3c9938634b490..bdcac24385621 100644 --- a/api_docs/kbn_securitysolution_ecs.mdx +++ b/api_docs/kbn_securitysolution_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-ecs title: "@kbn/securitysolution-ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-ecs plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-ecs'] --- import kbnSecuritysolutionEcsObj from './kbn_securitysolution_ecs.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index 4268bd21f81af..286bafc4be8ae 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-es-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-es-utils'] --- import kbnSecuritysolutionEsUtilsObj from './kbn_securitysolution_es_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_exception_list_components.mdx b/api_docs/kbn_securitysolution_exception_list_components.mdx index ea9e79cec7dbf..ced451bf44cf8 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.mdx +++ b/api_docs/kbn_securitysolution_exception_list_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-exception-list-components title: "@kbn/securitysolution-exception-list-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-exception-list-components plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-exception-list-components'] --- import kbnSecuritysolutionExceptionListComponentsObj from './kbn_securitysolution_exception_list_components.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_grouping.mdx b/api_docs/kbn_securitysolution_grouping.mdx index e3ca628a7c42e..134ded8959412 100644 --- a/api_docs/kbn_securitysolution_grouping.mdx +++ b/api_docs/kbn_securitysolution_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-grouping title: "@kbn/securitysolution-grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-grouping plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-grouping'] --- import kbnSecuritysolutionGroupingObj from './kbn_securitysolution_grouping.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index bcd97e2dd84ef..0f5e65cdca82d 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-hook-utils'] --- import kbnSecuritysolutionHookUtilsObj from './kbn_securitysolution_hook_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx index d928173ce5440..243de472d8d16 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-alerting-types'] --- import kbnSecuritysolutionIoTsAlertingTypesObj from './kbn_securitysolution_io_ts_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.mdx b/api_docs/kbn_securitysolution_io_ts_list_types.mdx index bf4e174dfded9..20c322ba72341 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-list-types'] --- import kbnSecuritysolutionIoTsListTypesObj from './kbn_securitysolution_io_ts_list_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_types.mdx b/api_docs/kbn_securitysolution_io_ts_types.mdx index 911ffe07505e0..86f58857a5b88 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-types'] --- import kbnSecuritysolutionIoTsTypesObj from './kbn_securitysolution_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_utils.mdx b/api_docs/kbn_securitysolution_io_ts_utils.mdx index b21fbeff5c570..fe7aafdde510a 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-utils'] --- import kbnSecuritysolutionIoTsUtilsObj from './kbn_securitysolution_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_api.mdx b/api_docs/kbn_securitysolution_list_api.mdx index f443ca6f68e2f..9351ba6a82274 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-api plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-api'] --- import kbnSecuritysolutionListApiObj from './kbn_securitysolution_list_api.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx index 81e2ff61db05c..bed2d7ceb892a 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-constants plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-constants'] --- import kbnSecuritysolutionListConstantsObj from './kbn_securitysolution_list_constants.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_hooks.mdx b/api_docs/kbn_securitysolution_list_hooks.mdx index 097841300a909..8864c0556d09c 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-hooks'] --- import kbnSecuritysolutionListHooksObj from './kbn_securitysolution_list_hooks.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_utils.mdx b/api_docs/kbn_securitysolution_list_utils.mdx index 341bb67f0408f..b771b4233129c 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-utils'] --- import kbnSecuritysolutionListUtilsObj from './kbn_securitysolution_list_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_rules.mdx b/api_docs/kbn_securitysolution_rules.mdx index 307a04514254f..48dd017d21cd8 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-rules plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-rules'] --- import kbnSecuritysolutionRulesObj from './kbn_securitysolution_rules.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_t_grid.mdx b/api_docs/kbn_securitysolution_t_grid.mdx index fcd16e9555d1c..e017156293a51 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-t-grid plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-t-grid'] --- import kbnSecuritysolutionTGridObj from './kbn_securitysolution_t_grid.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_utils.mdx b/api_docs/kbn_securitysolution_utils.mdx index 77a10967c2f88..ccd098e0cce09 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-utils'] --- import kbnSecuritysolutionUtilsObj from './kbn_securitysolution_utils.devdocs.json'; diff --git a/api_docs/kbn_server_http_tools.mdx b/api_docs/kbn_server_http_tools.mdx index abbf73d8236c6..b4937bf6ce69e 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-http-tools plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-http-tools'] --- import kbnServerHttpToolsObj from './kbn_server_http_tools.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository.mdx b/api_docs/kbn_server_route_repository.mdx index c24b60a20f957..60bf1fc183a50 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_serverless_project_switcher.mdx b/api_docs/kbn_serverless_project_switcher.mdx index 175977dbb6e22..eb5254b30807c 100644 --- a/api_docs/kbn_serverless_project_switcher.mdx +++ b/api_docs/kbn_serverless_project_switcher.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-project-switcher title: "@kbn/serverless-project-switcher" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-project-switcher plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-project-switcher'] --- import kbnServerlessProjectSwitcherObj from './kbn_serverless_project_switcher.devdocs.json'; diff --git a/api_docs/kbn_serverless_storybook_config.mdx b/api_docs/kbn_serverless_storybook_config.mdx index 21a2e8c25e275..3050200b77926 100644 --- a/api_docs/kbn_serverless_storybook_config.mdx +++ b/api_docs/kbn_serverless_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-storybook-config title: "@kbn/serverless-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-storybook-config plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-storybook-config'] --- import kbnServerlessStorybookConfigObj from './kbn_serverless_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index 35505d2bd1a9a..43a79bb804df9 100644 --- a/api_docs/kbn_shared_svg.mdx +++ b/api_docs/kbn_shared_svg.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-svg title: "@kbn/shared-svg" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-svg plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-svg'] --- import kbnSharedSvgObj from './kbn_shared_svg.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_solution.mdx b/api_docs/kbn_shared_ux_avatar_solution.mdx index 04dc5f3adbc90..365d382795851 100644 --- a/api_docs/kbn_shared_ux_avatar_solution.mdx +++ b/api_docs/kbn_shared_ux_avatar_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-solution title: "@kbn/shared-ux-avatar-solution" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-solution plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-solution'] --- import kbnSharedUxAvatarSolutionObj from './kbn_shared_ux_avatar_solution.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx index c369b145a8f0e..e3f3bc58ab2e3 100644 --- a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx +++ b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-user-profile-components title: "@kbn/shared-ux-avatar-user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-user-profile-components plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-user-profile-components'] --- import kbnSharedUxAvatarUserProfileComponentsObj from './kbn_shared_ux_avatar_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx index 3f567e4cff42a..bbf239c1a54f5 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen title: "@kbn/shared-ux-button-exit-full-screen" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen'] --- import kbnSharedUxButtonExitFullScreenObj from './kbn_shared_ux_button_exit_full_screen.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx index c52d19e9d35f6..c2ffcbecd4cbf 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen-mocks title: "@kbn/shared-ux-button-exit-full-screen-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen-mocks'] --- import kbnSharedUxButtonExitFullScreenMocksObj from './kbn_shared_ux_button_exit_full_screen_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index 0eb23afa59705..0adfb9feb1200 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-toolbar'] --- import kbnSharedUxButtonToolbarObj from './kbn_shared_ux_button_toolbar.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data.mdx b/api_docs/kbn_shared_ux_card_no_data.mdx index 17f6c66d58c89..4318ee625009d 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data title: "@kbn/shared-ux-card-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data'] --- import kbnSharedUxCardNoDataObj from './kbn_shared_ux_card_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx index eeb6f991db1d3..81ce041216314 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data-mocks title: "@kbn/shared-ux-card-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data-mocks'] --- import kbnSharedUxCardNoDataMocksObj from './kbn_shared_ux_card_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_chrome_navigation.mdx b/api_docs/kbn_shared_ux_chrome_navigation.mdx index cbb0de7496c59..c4513f0950273 100644 --- a/api_docs/kbn_shared_ux_chrome_navigation.mdx +++ b/api_docs/kbn_shared_ux_chrome_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-chrome-navigation title: "@kbn/shared-ux-chrome-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-chrome-navigation plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-chrome-navigation'] --- import kbnSharedUxChromeNavigationObj from './kbn_shared_ux_chrome_navigation.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_context.mdx b/api_docs/kbn_shared_ux_file_context.mdx index fe577411235cb..8822612effecf 100644 --- a/api_docs/kbn_shared_ux_file_context.mdx +++ b/api_docs/kbn_shared_ux_file_context.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-context title: "@kbn/shared-ux-file-context" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-context plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-context'] --- import kbnSharedUxFileContextObj from './kbn_shared_ux_file_context.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image.mdx b/api_docs/kbn_shared_ux_file_image.mdx index 858151006d8ce..b8e314847b7d2 100644 --- a/api_docs/kbn_shared_ux_file_image.mdx +++ b/api_docs/kbn_shared_ux_file_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image title: "@kbn/shared-ux-file-image" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image'] --- import kbnSharedUxFileImageObj from './kbn_shared_ux_file_image.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image_mocks.mdx b/api_docs/kbn_shared_ux_file_image_mocks.mdx index 247be558cbcef..54002fd5e231a 100644 --- a/api_docs/kbn_shared_ux_file_image_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_image_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image-mocks title: "@kbn/shared-ux-file-image-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image-mocks'] --- import kbnSharedUxFileImageMocksObj from './kbn_shared_ux_file_image_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_mocks.mdx b/api_docs/kbn_shared_ux_file_mocks.mdx index dbf4cef7d443b..4833a17b13646 100644 --- a/api_docs/kbn_shared_ux_file_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-mocks title: "@kbn/shared-ux-file-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-mocks'] --- import kbnSharedUxFileMocksObj from './kbn_shared_ux_file_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_picker.mdx b/api_docs/kbn_shared_ux_file_picker.mdx index ac3367f429743..69bf1ba9714c0 100644 --- a/api_docs/kbn_shared_ux_file_picker.mdx +++ b/api_docs/kbn_shared_ux_file_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-picker title: "@kbn/shared-ux-file-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-picker plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-picker'] --- import kbnSharedUxFilePickerObj from './kbn_shared_ux_file_picker.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_types.mdx b/api_docs/kbn_shared_ux_file_types.mdx index e2ed1b93b8fd3..603f55f21452c 100644 --- a/api_docs/kbn_shared_ux_file_types.mdx +++ b/api_docs/kbn_shared_ux_file_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-types title: "@kbn/shared-ux-file-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-types plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-types'] --- import kbnSharedUxFileTypesObj from './kbn_shared_ux_file_types.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_upload.mdx b/api_docs/kbn_shared_ux_file_upload.mdx index 2d77fc33b7d0f..9d35e93e55d58 100644 --- a/api_docs/kbn_shared_ux_file_upload.mdx +++ b/api_docs/kbn_shared_ux_file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-upload title: "@kbn/shared-ux-file-upload" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-upload plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-upload'] --- import kbnSharedUxFileUploadObj from './kbn_shared_ux_file_upload.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_util.mdx b/api_docs/kbn_shared_ux_file_util.mdx index a11a68075dc78..aea6d9ca9c944 100644 --- a/api_docs/kbn_shared_ux_file_util.mdx +++ b/api_docs/kbn_shared_ux_file_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-util title: "@kbn/shared-ux-file-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-util plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-util'] --- import kbnSharedUxFileUtilObj from './kbn_shared_ux_file_util.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app.mdx b/api_docs/kbn_shared_ux_link_redirect_app.mdx index 7b4d15989dbd4..73a24de7c582e 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app title: "@kbn/shared-ux-link-redirect-app" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app'] --- import kbnSharedUxLinkRedirectAppObj from './kbn_shared_ux_link_redirect_app.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx index f7aafa18bf14d..8d4e9f253a740 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app-mocks title: "@kbn/shared-ux-link-redirect-app-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app-mocks'] --- import kbnSharedUxLinkRedirectAppMocksObj from './kbn_shared_ux_link_redirect_app_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown.mdx b/api_docs/kbn_shared_ux_markdown.mdx index 5e009794043e1..3259792fa8a3e 100644 --- a/api_docs/kbn_shared_ux_markdown.mdx +++ b/api_docs/kbn_shared_ux_markdown.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown title: "@kbn/shared-ux-markdown" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown'] --- import kbnSharedUxMarkdownObj from './kbn_shared_ux_markdown.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown_mocks.mdx b/api_docs/kbn_shared_ux_markdown_mocks.mdx index 98bbbd98e3067..47995a01be940 100644 --- a/api_docs/kbn_shared_ux_markdown_mocks.mdx +++ b/api_docs/kbn_shared_ux_markdown_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown-mocks title: "@kbn/shared-ux-markdown-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown-mocks'] --- import kbnSharedUxMarkdownMocksObj from './kbn_shared_ux_markdown_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index e2048cf8bf66c..94ce217ad5251 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data title: "@kbn/shared-ux-page-analytics-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data'] --- import kbnSharedUxPageAnalyticsNoDataObj from './kbn_shared_ux_page_analytics_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx index 41baeb7fc84ef..b4208bdb934eb 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data-mocks title: "@kbn/shared-ux-page-analytics-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data-mocks'] --- import kbnSharedUxPageAnalyticsNoDataMocksObj from './kbn_shared_ux_page_analytics_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx index a77471c0ebd2b..a60e61262df8a 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data title: "@kbn/shared-ux-page-kibana-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data'] --- import kbnSharedUxPageKibanaNoDataObj from './kbn_shared_ux_page_kibana_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx index a20da4872dce3..c34c48ba5a2c4 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data-mocks title: "@kbn/shared-ux-page-kibana-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data-mocks'] --- import kbnSharedUxPageKibanaNoDataMocksObj from './kbn_shared_ux_page_kibana_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template.mdx b/api_docs/kbn_shared_ux_page_kibana_template.mdx index 67eb3fdc9d2da..5625062922f68 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template title: "@kbn/shared-ux-page-kibana-template" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template'] --- import kbnSharedUxPageKibanaTemplateObj from './kbn_shared_ux_page_kibana_template.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx index 5b09648ce1025..ae72e8f25ece2 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template-mocks title: "@kbn/shared-ux-page-kibana-template-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template-mocks'] --- import kbnSharedUxPageKibanaTemplateMocksObj from './kbn_shared_ux_page_kibana_template_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data.mdx b/api_docs/kbn_shared_ux_page_no_data.mdx index f34e531d7be38..cd7f501ef59bd 100644 --- a/api_docs/kbn_shared_ux_page_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data title: "@kbn/shared-ux-page-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data'] --- import kbnSharedUxPageNoDataObj from './kbn_shared_ux_page_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config.mdx b/api_docs/kbn_shared_ux_page_no_data_config.mdx index 784c60782d1b6..fb7deaf5ef3df 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config title: "@kbn/shared-ux-page-no-data-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config'] --- import kbnSharedUxPageNoDataConfigObj from './kbn_shared_ux_page_no_data_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx index 463881879d1d6..bf6a8f93ab49e 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config-mocks title: "@kbn/shared-ux-page-no-data-config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config-mocks'] --- import kbnSharedUxPageNoDataConfigMocksObj from './kbn_shared_ux_page_no_data_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx index a7ea6142f14a4..43cfd25a4b3a9 100644 --- a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-mocks title: "@kbn/shared-ux-page-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-mocks'] --- import kbnSharedUxPageNoDataMocksObj from './kbn_shared_ux_page_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_solution_nav.mdx b/api_docs/kbn_shared_ux_page_solution_nav.mdx index a9aee4d9a0987..176160b9a9613 100644 --- a/api_docs/kbn_shared_ux_page_solution_nav.mdx +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-solution-nav title: "@kbn/shared-ux-page-solution-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-solution-nav plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-solution-nav'] --- import kbnSharedUxPageSolutionNavObj from './kbn_shared_ux_page_solution_nav.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx index 7a28991b6db2f..7ec40f853f623 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views title: "@kbn/shared-ux-prompt-no-data-views" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views'] --- import kbnSharedUxPromptNoDataViewsObj from './kbn_shared_ux_prompt_no_data_views.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx index 4b9d7785fb315..7606162f3db6b 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views-mocks title: "@kbn/shared-ux-prompt-no-data-views-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views-mocks'] --- import kbnSharedUxPromptNoDataViewsMocksObj from './kbn_shared_ux_prompt_no_data_views_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_not_found.mdx b/api_docs/kbn_shared_ux_prompt_not_found.mdx index 7768c002563e0..926fcbffc93e8 100644 --- a/api_docs/kbn_shared_ux_prompt_not_found.mdx +++ b/api_docs/kbn_shared_ux_prompt_not_found.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-not-found title: "@kbn/shared-ux-prompt-not-found" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-not-found plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-not-found'] --- import kbnSharedUxPromptNotFoundObj from './kbn_shared_ux_prompt_not_found.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router.mdx b/api_docs/kbn_shared_ux_router.mdx index 0a4267873f834..e0e6eb404306d 100644 --- a/api_docs/kbn_shared_ux_router.mdx +++ b/api_docs/kbn_shared_ux_router.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router title: "@kbn/shared-ux-router" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router'] --- import kbnSharedUxRouterObj from './kbn_shared_ux_router.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router_mocks.mdx b/api_docs/kbn_shared_ux_router_mocks.mdx index 460135a134d2a..8ab5f358d30b9 100644 --- a/api_docs/kbn_shared_ux_router_mocks.mdx +++ b/api_docs/kbn_shared_ux_router_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router-mocks title: "@kbn/shared-ux-router-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router-mocks plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router-mocks'] --- import kbnSharedUxRouterMocksObj from './kbn_shared_ux_router_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_config.mdx b/api_docs/kbn_shared_ux_storybook_config.mdx index 33d552a1318eb..c40f63fd15ba6 100644 --- a/api_docs/kbn_shared_ux_storybook_config.mdx +++ b/api_docs/kbn_shared_ux_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-config title: "@kbn/shared-ux-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-config plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-config'] --- import kbnSharedUxStorybookConfigObj from './kbn_shared_ux_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_mock.mdx b/api_docs/kbn_shared_ux_storybook_mock.mdx index 149982eb03f10..9c12c13d09d0e 100644 --- a/api_docs/kbn_shared_ux_storybook_mock.mdx +++ b/api_docs/kbn_shared_ux_storybook_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-mock title: "@kbn/shared-ux-storybook-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-mock plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-mock'] --- import kbnSharedUxStorybookMockObj from './kbn_shared_ux_storybook_mock.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index 1cbc67ee4a11e..b6b5e86ea67e0 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-utility plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_slo_schema.mdx b/api_docs/kbn_slo_schema.mdx index b085b172ac32e..5bde2ad8365f7 100644 --- a/api_docs/kbn_slo_schema.mdx +++ b/api_docs/kbn_slo_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-slo-schema title: "@kbn/slo-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/slo-schema plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/slo-schema'] --- import kbnSloSchemaObj from './kbn_slo_schema.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index d7269e4f87a70..66f26519d1ab6 100644 --- a/api_docs/kbn_some_dev_log.mdx +++ b/api_docs/kbn_some_dev_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-some-dev-log title: "@kbn/some-dev-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/some-dev-log plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index 3ee76d8ce57bb..815a97c1dfffe 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/std plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/std'] --- import kbnStdObj from './kbn_std.devdocs.json'; diff --git a/api_docs/kbn_stdio_dev_helpers.mdx b/api_docs/kbn_stdio_dev_helpers.mdx index 8951131d9c1b3..9507e46669d50 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/stdio-dev-helpers plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/stdio-dev-helpers'] --- import kbnStdioDevHelpersObj from './kbn_stdio_dev_helpers.devdocs.json'; diff --git a/api_docs/kbn_storybook.mdx b/api_docs/kbn_storybook.mdx index 94802cce7aabc..96b4e7cb42636 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/storybook plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index b9c5a4ed5a588..e10964750f681 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/telemetry-tools plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/telemetry-tools'] --- import kbnTelemetryToolsObj from './kbn_telemetry_tools.devdocs.json'; diff --git a/api_docs/kbn_test.mdx b/api_docs/kbn_test.mdx index a28269b45d0fc..16e559f3cd948 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index 56e27ec9a1ea4..5386812542a2d 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-jest-helpers plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-jest-helpers'] --- import kbnTestJestHelpersObj from './kbn_test_jest_helpers.devdocs.json'; diff --git a/api_docs/kbn_test_subj_selector.mdx b/api_docs/kbn_test_subj_selector.mdx index 02d2aadd706f9..d8c75ae07419a 100644 --- a/api_docs/kbn_test_subj_selector.mdx +++ b/api_docs/kbn_test_subj_selector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-subj-selector title: "@kbn/test-subj-selector" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-subj-selector plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_text_based_editor.mdx b/api_docs/kbn_text_based_editor.mdx index 5daf28cd7d244..f37190ddc1e0f 100644 --- a/api_docs/kbn_text_based_editor.mdx +++ b/api_docs/kbn_text_based_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-text-based-editor title: "@kbn/text-based-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/text-based-editor plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/text-based-editor'] --- import kbnTextBasedEditorObj from './kbn_text_based_editor.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index 7ee9dd44938f0..101042ea6c065 100644 --- a/api_docs/kbn_tooling_log.mdx +++ b/api_docs/kbn_tooling_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log title: "@kbn/tooling-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/tooling-log plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_ts_projects.mdx b/api_docs/kbn_ts_projects.mdx index 6104ee6e57500..b4fcaea9aecd0 100644 --- a/api_docs/kbn_ts_projects.mdx +++ b/api_docs/kbn_ts_projects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ts-projects title: "@kbn/ts-projects" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ts-projects plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ts-projects'] --- import kbnTsProjectsObj from './kbn_ts_projects.devdocs.json'; diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index f57367798f2aa..0bf8cb2d6e039 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/typed-react-router-config plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/typed-react-router-config'] --- import kbnTypedReactRouterConfigObj from './kbn_typed_react_router_config.devdocs.json'; diff --git a/api_docs/kbn_ui_actions_browser.mdx b/api_docs/kbn_ui_actions_browser.mdx index 032c531cca7f6..23560795684df 100644 --- a/api_docs/kbn_ui_actions_browser.mdx +++ b/api_docs/kbn_ui_actions_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-actions-browser title: "@kbn/ui-actions-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-actions-browser plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-actions-browser'] --- import kbnUiActionsBrowserObj from './kbn_ui_actions_browser.devdocs.json'; diff --git a/api_docs/kbn_ui_shared_deps_src.mdx b/api_docs/kbn_ui_shared_deps_src.mdx index d9303de280b06..07cd8dbb085ff 100644 --- a/api_docs/kbn_ui_shared_deps_src.mdx +++ b/api_docs/kbn_ui_shared_deps_src.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-shared-deps-src title: "@kbn/ui-shared-deps-src" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-shared-deps-src plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-shared-deps-src'] --- import kbnUiSharedDepsSrcObj from './kbn_ui_shared_deps_src.devdocs.json'; diff --git a/api_docs/kbn_ui_theme.mdx b/api_docs/kbn_ui_theme.mdx index a6390418bffdc..c445dd46160a8 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-theme plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_unified_field_list.mdx b/api_docs/kbn_unified_field_list.mdx index 3a57dc997ac59..5fbabd339666d 100644 --- a/api_docs/kbn_unified_field_list.mdx +++ b/api_docs/kbn_unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-field-list title: "@kbn/unified-field-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-field-list plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-field-list'] --- import kbnUnifiedFieldListObj from './kbn_unified_field_list.devdocs.json'; diff --git a/api_docs/kbn_url_state.mdx b/api_docs/kbn_url_state.mdx index d20cd31a79a8f..e8551189096d5 100644 --- a/api_docs/kbn_url_state.mdx +++ b/api_docs/kbn_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-url-state title: "@kbn/url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/url-state plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/url-state'] --- import kbnUrlStateObj from './kbn_url_state.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index f677f876d2f8e..fde8c7f3edeb7 100644 --- a/api_docs/kbn_user_profile_components.mdx +++ b/api_docs/kbn_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-user-profile-components title: "@kbn/user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/user-profile-components plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/user-profile-components'] --- import kbnUserProfileComponentsObj from './kbn_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_utility_types.mdx b/api_docs/kbn_utility_types.mdx index 95b56c597ae28..d911e61e165d3 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types'] --- import kbnUtilityTypesObj from './kbn_utility_types.devdocs.json'; diff --git a/api_docs/kbn_utility_types_jest.mdx b/api_docs/kbn_utility_types_jest.mdx index c46479a9ff44d..d322837209b81 100644 --- a/api_docs/kbn_utility_types_jest.mdx +++ b/api_docs/kbn_utility_types_jest.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest title: "@kbn/utility-types-jest" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types-jest plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types-jest'] --- import kbnUtilityTypesJestObj from './kbn_utility_types_jest.devdocs.json'; diff --git a/api_docs/kbn_utils.mdx b/api_docs/kbn_utils.mdx index 9813607dc45aa..d48c3e8f348b1 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index 36757ad29f0f3..d9e75497bad3f 100644 --- a/api_docs/kbn_yarn_lock_validator.mdx +++ b/api_docs/kbn_yarn_lock_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-yarn-lock-validator title: "@kbn/yarn-lock-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/yarn-lock-validator plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index e794ae8bd88ba..ee7ba8f9abebf 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaOverview plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaOverview'] --- import kibanaOverviewObj from './kibana_overview.devdocs.json'; diff --git a/api_docs/kibana_react.mdx b/api_docs/kibana_react.mdx index 1cc1cc3031f0d..c5ff29da175ea 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaReact plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaReact'] --- import kibanaReactObj from './kibana_react.devdocs.json'; diff --git a/api_docs/kibana_utils.mdx b/api_docs/kibana_utils.mdx index 25766d27a39fe..28a0faf0b7a93 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaUtils plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaUtils'] --- import kibanaUtilsObj from './kibana_utils.devdocs.json'; diff --git a/api_docs/kubernetes_security.mdx b/api_docs/kubernetes_security.mdx index 5e648fe9f9299..ac0766b2756c1 100644 --- a/api_docs/kubernetes_security.mdx +++ b/api_docs/kubernetes_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kubernetesSecurity title: "kubernetesSecurity" image: https://source.unsplash.com/400x175/?github description: API docs for the kubernetesSecurity plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.devdocs.json b/api_docs/lens.devdocs.json index a3dca011ebc79..e151c4ab1fe45 100644 --- a/api_docs/lens.devdocs.json +++ b/api_docs/lens.devdocs.json @@ -8593,7 +8593,7 @@ "CustomXDomain", " | undefined>; ariaDescription?: string | undefined; ariaDescribedBy?: string | undefined; ariaLabelledBy?: string | undefined; ariaTableCaption?: string | undefined; legendAction?: \"ignore\" | undefined; legendStrategy?: ", "LegendStrategy", - " | undefined; onLegendItemClick?: \"ignore\" | undefined; customLegend?: \"ignore\" | undefined; onLegendItemMinusClick?: \"ignore\" | undefined; onLegendItemOut?: \"ignore\" | undefined; onLegendItemOver?: \"ignore\" | undefined; onLegendItemPlusClick?: \"ignore\" | undefined; debugState?: boolean | undefined; onProjectionClick?: \"ignore\" | undefined; onElementClick?: \"ignore\" | undefined; onElementOver?: \"ignore\" | undefined; onElementOut?: \"ignore\" | undefined; onBrushEnd?: \"ignore\" | undefined; onProjectionAreaChange?: \"ignore\" | undefined; onAnnotationClick?: \"ignore\" | undefined; pointerUpdateDebounce?: number | undefined; roundHistogramBrushValues?: boolean | undefined; noResults?: React.ReactChild | React.ComponentType<{}> | undefined; legendSort?: \"ignore\" | undefined; }>>) | undefined" + " | undefined; onLegendItemClick?: \"ignore\" | undefined; customLegend?: \"ignore\" | undefined; onLegendItemMinusClick?: \"ignore\" | undefined; onLegendItemOut?: \"ignore\" | undefined; onLegendItemOver?: \"ignore\" | undefined; onLegendItemPlusClick?: \"ignore\" | undefined; debugState?: boolean | undefined; onProjectionClick?: \"ignore\" | undefined; onElementClick?: \"ignore\" | undefined; onElementOver?: \"ignore\" | undefined; onElementOut?: \"ignore\" | undefined; onBrushEnd?: \"ignore\" | undefined; onProjectionAreaChange?: \"ignore\" | undefined; onAnnotationClick?: \"ignore\" | undefined; pointerUpdateDebounce?: number | undefined; roundHistogramBrushValues?: boolean | undefined; noResults?: React.ReactChild | React.ComponentType<{}> | undefined; legendSort?: \"ignore\" | undefined; }>> & Partial>) | undefined" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts", "deprecated": false, diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index 2d6aeddb76182..0a1c3bae11104 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lens title: "lens" image: https://source.unsplash.com/400x175/?github description: API docs for the lens plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index f804fffc159a2..9225b262dbe9f 100644 --- a/api_docs/license_api_guard.mdx +++ b/api_docs/license_api_guard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard title: "licenseApiGuard" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseApiGuard plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseApiGuard'] --- import licenseApiGuardObj from './license_api_guard.devdocs.json'; diff --git a/api_docs/license_management.mdx b/api_docs/license_management.mdx index 658e0af2a18ce..8de872753892a 100644 --- a/api_docs/license_management.mdx +++ b/api_docs/license_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseManagement title: "licenseManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseManagement plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseManagement'] --- import licenseManagementObj from './license_management.devdocs.json'; diff --git a/api_docs/licensing.mdx b/api_docs/licensing.mdx index afdf76d7d70ab..047070d494b97 100644 --- a/api_docs/licensing.mdx +++ b/api_docs/licensing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licensing title: "licensing" image: https://source.unsplash.com/400x175/?github description: API docs for the licensing plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index c6a99e06ba8e1..bbaaad6c45db5 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lists title: "lists" image: https://source.unsplash.com/400x175/?github description: API docs for the lists plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/management.devdocs.json b/api_docs/management.devdocs.json index 4ca34adc10ea7..6e970de9f2c57 100644 --- a/api_docs/management.devdocs.json +++ b/api_docs/management.devdocs.json @@ -804,6 +804,40 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "management", + "id": "def-public.ManagementStart.setupCardsNavigation", + "type": "Function", + "tags": [], + "label": "setupCardsNavigation", + "description": [], + "signature": [ + "({ enabled, hideLinksTo }: ", + "NavigationCardsSubject", + ") => void" + ], + "path": "src/plugins/management/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "management", + "id": "def-public.ManagementStart.setupCardsNavigation.$1", + "type": "Object", + "tags": [], + "label": "{ enabled, hideLinksTo }", + "description": [], + "signature": [ + "NavigationCardsSubject" + ], + "path": "src/plugins/management/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] } ], "lifecycle": "start", diff --git a/api_docs/management.mdx b/api_docs/management.mdx index b2a35b6b9817a..6f730945cc4ad 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github description: API docs for the management plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'management'] --- import managementObj from './management.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/platform-deployment-management](https://github.com/orgs/elasti | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 43 | 0 | 43 | 6 | +| 45 | 0 | 45 | 7 | ## Client diff --git a/api_docs/maps.mdx b/api_docs/maps.mdx index dad1b50e46330..7dce59669f358 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github description: API docs for the maps plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'maps'] --- import mapsObj from './maps.devdocs.json'; diff --git a/api_docs/maps_ems.mdx b/api_docs/maps_ems.mdx index 01e4450d3db40..254967debb785 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github description: API docs for the mapsEms plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/ml.devdocs.json b/api_docs/ml.devdocs.json index 77287b57334ec..52fbc37e7e2a3 100644 --- a/api_docs/ml.devdocs.json +++ b/api_docs/ml.devdocs.json @@ -462,7 +462,7 @@ "label": "capabilities", "description": [], "signature": [ - "{ canGetJobs: boolean; canGetDatafeeds: boolean; canGetCalendars: boolean; canFindFileStructure: boolean; canGetDataFrameAnalytics: boolean; canGetAnnotations: boolean; canCreateAnnotation: boolean; canDeleteAnnotation: boolean; canUseMlAlerts: boolean; canGetTrainedModels: boolean; canTestTrainedModels: boolean; canGetFieldInfo: boolean; canGetMlInfo: boolean; canUseAiops: boolean; } & { canCreateJob: boolean; canDeleteJob: boolean; canOpenJob: boolean; canCloseJob: boolean; canResetJob: boolean; canUpdateJob: boolean; canForecastJob: boolean; canCreateDatafeed: boolean; canDeleteDatafeed: boolean; canStartStopDatafeed: boolean; canUpdateDatafeed: boolean; canPreviewDatafeed: boolean; canGetFilters: boolean; canCreateCalendar: boolean; canDeleteCalendar: boolean; canCreateFilter: boolean; canDeleteFilter: boolean; canCreateDataFrameAnalytics: boolean; canDeleteDataFrameAnalytics: boolean; canStartStopDataFrameAnalytics: boolean; canCreateMlAlerts: boolean; canUseMlAlerts: boolean; canViewMlNodes: boolean; canCreateTrainedModels: boolean; canDeleteTrainedModels: boolean; canStartStopTrainedModels: boolean; }" + "{ isADEnabled: boolean; isDFAEnabled: boolean; isNLPEnabled: boolean; } & { canGetJobs: boolean; canGetDatafeeds: boolean; canGetCalendars: boolean; canFindFileStructure: boolean; canGetDataFrameAnalytics: boolean; canGetAnnotations: boolean; canCreateAnnotation: boolean; canDeleteAnnotation: boolean; canUseMlAlerts: boolean; canGetTrainedModels: boolean; canTestTrainedModels: boolean; canGetFieldInfo: boolean; canGetMlInfo: boolean; canUseAiops: boolean; } & { canCreateJob: boolean; canDeleteJob: boolean; canOpenJob: boolean; canCloseJob: boolean; canResetJob: boolean; canUpdateJob: boolean; canForecastJob: boolean; canCreateDatafeed: boolean; canDeleteDatafeed: boolean; canStartStopDatafeed: boolean; canUpdateDatafeed: boolean; canPreviewDatafeed: boolean; canGetFilters: boolean; canCreateCalendar: boolean; canDeleteCalendar: boolean; canCreateFilter: boolean; canDeleteFilter: boolean; canCreateDataFrameAnalytics: boolean; canDeleteDataFrameAnalytics: boolean; canStartStopDataFrameAnalytics: boolean; canCreateMlAlerts: boolean; canUseMlAlerts: boolean; canViewMlNodes: boolean; canCreateTrainedModels: boolean; canDeleteTrainedModels: boolean; canStartStopTrainedModels: boolean; }" ], "path": "x-pack/plugins/ml/common/types/capabilities.ts", "deprecated": false, @@ -1915,7 +1915,8 @@ "): { preview: (args_0: Readonly<{} & { timeRange: string; alertParams: Readonly<{} & { severity: number; jobSelection: Readonly<{} & { groupIds: string[]; jobIds: string[]; }>; resultType: \"bucket\" | \"record\" | \"influencer\"; includeInterim: boolean; lookbackInterval: string | null; topNBuckets: number | null; }>; sampleSize: number; }>) => Promise; execute: (params: Readonly<{} & { severity: number; jobSelection: Readonly<{} & { groupIds: string[]; jobIds: string[]; }>; resultType: \"bucket\" | \"record\" | \"influencer\"; includeInterim: boolean; lookbackInterval: string | null; topNBuckets: number | null; }>) => Promise<{ context: ", "AnomalyDetectionAlertContext", "; name: string; isHealthy: boolean; } | undefined>; }; } & ", - "TrainedModelsProvider" + "TrainedModelsProvider", + " & MlSetup" ], "path": "x-pack/plugins/ml/server/plugin.ts", "deprecated": false, diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index bb16b0f9297d3..a7c468d0acbdf 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github description: API docs for the ml plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) for questi | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 150 | 3 | 64 | 33 | +| 150 | 3 | 64 | 32 | ## Client diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index 051fd4068fd1c..ca697dc1cc9ec 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoring plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoring'] --- import monitoringObj from './monitoring.devdocs.json'; diff --git a/api_docs/monitoring_collection.mdx b/api_docs/monitoring_collection.mdx index f1330e88a88df..f7df9e858e768 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoringCollection plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] --- import monitoringCollectionObj from './monitoring_collection.devdocs.json'; diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index 64f059808d001..40c69346b9445 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the navigation plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] --- import navigationObj from './navigation.devdocs.json'; diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index 4228c5757a399..a381c4195c2d9 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github description: API docs for the newsfeed plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/notifications.mdx b/api_docs/notifications.mdx index d42daba287f87..72fe7e558062f 100644 --- a/api_docs/notifications.mdx +++ b/api_docs/notifications.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/notifications title: "notifications" image: https://source.unsplash.com/400x175/?github description: API docs for the notifications plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'notifications'] --- import notificationsObj from './notifications.devdocs.json'; diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index f659ad371e458..19b04cca8047c 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github description: API docs for the observability plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/observability_onboarding.mdx b/api_docs/observability_onboarding.mdx index f537d38868270..7cccdcb4a8c98 100644 --- a/api_docs/observability_onboarding.mdx +++ b/api_docs/observability_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityOnboarding title: "observabilityOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityOnboarding plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityOnboarding'] --- import observabilityOnboardingObj from './observability_onboarding.devdocs.json'; diff --git a/api_docs/observability_shared.mdx b/api_docs/observability_shared.mdx index 70f73a7877b81..45d01be1c6443 100644 --- a/api_docs/observability_shared.mdx +++ b/api_docs/observability_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityShared title: "observabilityShared" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityShared plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityShared'] --- import observabilitySharedObj from './observability_shared.devdocs.json'; diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 003296222d067..0c3e65f60d78a 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github description: API docs for the osquery plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index 51ac1717b3c95..713bd0c62c1e2 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -7,7 +7,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory description: Directory of public APIs available through plugins or packages. -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -15,30 +15,30 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Count | Plugins or Packages with a
public API | Number of teams | |--------------|----------|------------------------| -| 642 | 533 | 38 | +| 645 | 536 | 38 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 70934 | 544 | 60753 | 1406 | +| 70960 | 544 | 60773 | 1410 | ## Plugin Directory | Plugin name           | Maintaining team | Description | API Cnt | Any Cnt | Missing
comments | Missing
exports | |--------------|----------------|-----------|--------------|----------|---------------|--------| -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 268 | 10 | 263 | 27 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 270 | 10 | 265 | 27 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 36 | 1 | 32 | 2 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | AIOps plugin maintained by ML team. | 45 | 0 | 27 | 1 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 635 | 1 | 611 | 47 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 619 | 1 | 595 | 47 | | | [@elastic/apm-ui](https://github.com/orgs/elastic/teams/apm-ui) | The user interface for Elastic APM | 48 | 0 | 48 | 113 | | | [@elastic/infra-monitoring-ui](https://github.com/orgs/elastic/teams/infra-monitoring-ui) | Asset manager plugin for entity assets (inventory, topology, etc) | 3 | 0 | 3 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 9 | 0 | 9 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Considering using bfetch capabilities when fetching large amounts of data. This services supports batching HTTP requests and streaming responses back. | 91 | 1 | 75 | 2 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds Canvas application to Kibana | 9 | 0 | 8 | 3 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | The Case management system in Kibana | 80 | 0 | 65 | 26 | -| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 271 | 16 | 256 | 10 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 52 | 0 | 11 | 0 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 268 | 16 | 253 | 10 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 54 | 0 | 12 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | Chat available on Elastic Cloud deployments for quicker assistance. | 3 | 0 | 2 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | This plugin exists as a workaround for using `cloudChat` plugin in plugins which can't have a direct dependency on security plugin. | 5 | 0 | 5 | 0 | | | [@elastic/platform-onboarding](https://github.com/orgs/elastic/teams/platform-onboarding) | Static migration page where self-managed users can see text/copy about migrating to Elastic Cloud | 8 | 1 | 8 | 1 | @@ -123,10 +123,10 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 117 | 0 | 42 | 10 | | | [@elastic/security-detection-engine](https://github.com/orgs/elastic/teams/security-detection-engine) | - | 210 | 0 | 94 | 51 | | logstash | [@elastic/logstash](https://github.com/orgs/elastic/teams/logstash) | - | 0 | 0 | 0 | 0 | -| | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 43 | 0 | 43 | 6 | +| | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 45 | 0 | 45 | 7 | | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | - | 266 | 0 | 265 | 28 | | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | - | 67 | 0 | 67 | 0 | -| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the machine learning features provided by Elastic. | 150 | 3 | 64 | 33 | +| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the machine learning features provided by Elastic. | 150 | 3 | 64 | 32 | | | [@elastic/infra-monitoring-ui](https://github.com/orgs/elastic/teams/infra-monitoring-ui) | - | 15 | 3 | 13 | 1 | | | [@elastic/infra-monitoring-ui](https://github.com/orgs/elastic/teams/infra-monitoring-ui) | - | 9 | 0 | 9 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 34 | 0 | 34 | 2 | @@ -177,7 +177,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 257 | 1 | 213 | 22 | | | [@elastic/ml-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 | [@elastic/kibana-localization](https://github.com/orgs/elastic/teams/kibana-localization) | - | 0 | 0 | 0 | 0 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 547 | 11 | 521 | 49 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 547 | 11 | 521 | 50 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Adds UI Actions service to Kibana | 144 | 2 | 102 | 9 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Extends UI Actions plugin with more functionality | 206 | 0 | 140 | 9 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | The `unifiedHistogram` plugin provides UI components to create a layout including a resizable histogram and a main display. | 52 | 0 | 23 | 2 | @@ -228,7 +228,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/apm-ui](https://github.com/orgs/elastic/teams/apm-ui) | - | 11 | 0 | 11 | 0 | | | [@elastic/kibana-qa](https://github.com/orgs/elastic/teams/kibana-qa) | - | 12 | 0 | 12 | 0 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 19 | 0 | 17 | 0 | -| | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 62 | 1 | 44 | 2 | +| | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 62 | 1 | 44 | 3 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 11 | 0 | 8 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 78 | 0 | 78 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 7 | 0 | 2 | 0 | @@ -267,7 +267,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 5 | 0 | 0 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 16 | 0 | 7 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 6 | 0 | 6 | 0 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 157 | 0 | 61 | 0 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 166 | 0 | 67 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 3 | 0 | 3 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 8 | 0 | 8 | 0 | @@ -371,7 +371,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 73 | 0 | 40 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 26 | 0 | 23 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 123 | 0 | 89 | 46 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 124 | 0 | 90 | 46 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 12 | 0 | 12 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 535 | 1 | 115 | 4 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 69 | 0 | 69 | 4 | @@ -422,7 +422,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 101 | 0 | 85 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 15 | 0 | 9 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 27 | 2 | 24 | 0 | -| | [@elastic/docs](https://github.com/orgs/elastic/teams/docs) | - | 71 | 0 | 71 | 2 | +| | [@elastic/docs](https://github.com/orgs/elastic/teams/docs) | - | 72 | 0 | 72 | 2 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 1 | 0 | 1 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 52 | 0 | 34 | 4 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 19 | 0 | 11 | 0 | @@ -463,6 +463,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 27 | 0 | 1 | 2 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 8 | 0 | 8 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 6 | 0 | 1 | 1 | +| | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 10 | 0 | 10 | 1 | +| | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 2 | 0 | 0 | 0 | | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | - | 535 | 1 | 1 | 0 | | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | - | 2 | 0 | 2 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 96 | 2 | 61 | 0 | @@ -502,6 +504,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 6 | 0 | 6 | 1 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 73 | 0 | 65 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 13 | 2 | 8 | 0 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 16 | 0 | 16 | 1 | | | [@elastic/security-detections-response](https://github.com/orgs/elastic/teams/security-detections-response) | - | 107 | 0 | 104 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 2 | 0 | | | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 41 | 0 | 35 | 0 | diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index 1eeb613f16ae5..18f78a4ea2427 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationUtil plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] --- import presentationUtilObj from './presentation_util.devdocs.json'; diff --git a/api_docs/profiling.mdx b/api_docs/profiling.mdx index bc9128a687c97..ae042466b51ee 100644 --- a/api_docs/profiling.mdx +++ b/api_docs/profiling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profiling title: "profiling" image: https://source.unsplash.com/400x175/?github description: API docs for the profiling plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index 8c6210ad985e6..cdf5e69b2a7bf 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github description: API docs for the remoteClusters plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'remoteClusters'] --- import remoteClustersObj from './remote_clusters.devdocs.json'; diff --git a/api_docs/reporting.mdx b/api_docs/reporting.mdx index 0ace0410ae071..bde1754b6d36b 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github description: API docs for the reporting plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'reporting'] --- import reportingObj from './reporting.devdocs.json'; diff --git a/api_docs/reporting_export_types.mdx b/api_docs/reporting_export_types.mdx index c2e8d52c452a0..666e74dc24ee1 100644 --- a/api_docs/reporting_export_types.mdx +++ b/api_docs/reporting_export_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/reportingExportTypes title: "reportingExportTypes" image: https://source.unsplash.com/400x175/?github description: API docs for the reportingExportTypes plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'reportingExportTypes'] --- import reportingExportTypesObj from './reporting_export_types.devdocs.json'; diff --git a/api_docs/rollup.mdx b/api_docs/rollup.mdx index 1f587ce70bc3e..56ef3a6a083b1 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the rollup plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] --- import rollupObj from './rollup.devdocs.json'; diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index 84ee443e918cb..0e2f5fa7c3ca2 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github description: API docs for the ruleRegistry plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ruleRegistry'] --- import ruleRegistryObj from './rule_registry.devdocs.json'; diff --git a/api_docs/runtime_fields.mdx b/api_docs/runtime_fields.mdx index 4dba794d349ef..4b755988ed70f 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github description: API docs for the runtimeFields plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'runtimeFields'] --- import runtimeFieldsObj from './runtime_fields.devdocs.json'; diff --git a/api_docs/saved_objects.mdx b/api_docs/saved_objects.mdx index 09365c173b96d..745cc7089a5f7 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjects plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjects'] --- import savedObjectsObj from './saved_objects.devdocs.json'; diff --git a/api_docs/saved_objects_finder.mdx b/api_docs/saved_objects_finder.mdx index 9855c48ba90d0..24f360208256f 100644 --- a/api_docs/saved_objects_finder.mdx +++ b/api_docs/saved_objects_finder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsFinder title: "savedObjectsFinder" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsFinder plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsFinder'] --- import savedObjectsFinderObj from './saved_objects_finder.devdocs.json'; diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx index a5ca9791cb2a1..31ca9217da8fa 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsManagement plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsManagement'] --- import savedObjectsManagementObj from './saved_objects_management.devdocs.json'; diff --git a/api_docs/saved_objects_tagging.mdx b/api_docs/saved_objects_tagging.mdx index 08fa5f5248fb1..e621382187582 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTagging plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTagging'] --- import savedObjectsTaggingObj from './saved_objects_tagging.devdocs.json'; diff --git a/api_docs/saved_objects_tagging_oss.mdx b/api_docs/saved_objects_tagging_oss.mdx index 21a0222377b4f..ca16449b6e8bd 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTaggingOss plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTaggingOss'] --- import savedObjectsTaggingOssObj from './saved_objects_tagging_oss.devdocs.json'; diff --git a/api_docs/saved_search.mdx b/api_docs/saved_search.mdx index 5ac6c566d99da..ddd11a2636676 100644 --- a/api_docs/saved_search.mdx +++ b/api_docs/saved_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedSearch title: "savedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the savedSearch plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedSearch'] --- import savedSearchObj from './saved_search.devdocs.json'; diff --git a/api_docs/screenshot_mode.mdx b/api_docs/screenshot_mode.mdx index 72396d4b8a61e..f273270e2ac70 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotMode plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotMode'] --- import screenshotModeObj from './screenshot_mode.devdocs.json'; diff --git a/api_docs/screenshotting.mdx b/api_docs/screenshotting.mdx index e1235d8166475..eeecae3ee29db 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotting plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index 853737ae7926c..57a720991cc39 100644 --- a/api_docs/security.mdx +++ b/api_docs/security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/security title: "security" image: https://source.unsplash.com/400x175/?github description: API docs for the security plugin -date: 2023-06-30 +date: 2023-07-03 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 48e99976757b0..5a543aeb6ccf5 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolution title: "securitySolution" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolution plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/serverless.mdx b/api_docs/serverless.mdx index fa97a61d51f97..2442708db81a7 100644 --- a/api_docs/serverless.mdx +++ b/api_docs/serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverless title: "serverless" image: https://source.unsplash.com/400x175/?github description: API docs for the serverless plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverless'] --- import serverlessObj from './serverless.devdocs.json'; diff --git a/api_docs/serverless_observability.mdx b/api_docs/serverless_observability.mdx index 841267119dd75..3c8c535fcfdf6 100644 --- a/api_docs/serverless_observability.mdx +++ b/api_docs/serverless_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessObservability title: "serverlessObservability" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessObservability plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessObservability'] --- import serverlessObservabilityObj from './serverless_observability.devdocs.json'; diff --git a/api_docs/serverless_search.mdx b/api_docs/serverless_search.mdx index f4f8947f7fb95..f9592d2e85309 100644 --- a/api_docs/serverless_search.mdx +++ b/api_docs/serverless_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessSearch title: "serverlessSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessSearch plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessSearch'] --- import serverlessSearchObj from './serverless_search.devdocs.json'; diff --git a/api_docs/serverless_security.mdx b/api_docs/serverless_security.mdx index 83f20833a9989..f3c01d3ce6021 100644 --- a/api_docs/serverless_security.mdx +++ b/api_docs/serverless_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessSecurity title: "serverlessSecurity" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessSecurity plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessSecurity'] --- import serverlessSecurityObj from './serverless_security.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index 5b36b725abe61..d2fde45c2ea69 100644 --- a/api_docs/session_view.mdx +++ b/api_docs/session_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/sessionView title: "sessionView" image: https://source.unsplash.com/400x175/?github description: API docs for the sessionView plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'sessionView'] --- import sessionViewObj from './session_view.devdocs.json'; diff --git a/api_docs/share.mdx b/api_docs/share.mdx index 3c9dae710a208..7e960f2472051 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/share title: "share" image: https://source.unsplash.com/400x175/?github description: API docs for the share plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index d64d97325ed71..a0b069f790b58 100644 --- a/api_docs/snapshot_restore.mdx +++ b/api_docs/snapshot_restore.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/snapshotRestore title: "snapshotRestore" image: https://source.unsplash.com/400x175/?github description: API docs for the snapshotRestore plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'snapshotRestore'] --- import snapshotRestoreObj from './snapshot_restore.devdocs.json'; diff --git a/api_docs/spaces.mdx b/api_docs/spaces.mdx index 67ce44f89e682..6bd068ccfb441 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/spaces title: "spaces" image: https://source.unsplash.com/400x175/?github description: API docs for the spaces plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'spaces'] --- import spacesObj from './spaces.devdocs.json'; diff --git a/api_docs/stack_alerts.mdx b/api_docs/stack_alerts.mdx index cb6387b7e76c0..871a33484bd3d 100644 --- a/api_docs/stack_alerts.mdx +++ b/api_docs/stack_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackAlerts title: "stackAlerts" image: https://source.unsplash.com/400x175/?github description: API docs for the stackAlerts plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackAlerts'] --- import stackAlertsObj from './stack_alerts.devdocs.json'; diff --git a/api_docs/stack_connectors.mdx b/api_docs/stack_connectors.mdx index fe24915d6997e..26dd11a63e519 100644 --- a/api_docs/stack_connectors.mdx +++ b/api_docs/stack_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackConnectors title: "stackConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the stackConnectors plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackConnectors'] --- import stackConnectorsObj from './stack_connectors.devdocs.json'; diff --git a/api_docs/task_manager.mdx b/api_docs/task_manager.mdx index 582f37104d947..06ea30b7ee13a 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/taskManager title: "taskManager" image: https://source.unsplash.com/400x175/?github description: API docs for the taskManager plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'taskManager'] --- import taskManagerObj from './task_manager.devdocs.json'; diff --git a/api_docs/telemetry.mdx b/api_docs/telemetry.mdx index 24cf8c0959585..cbc4dcc929706 100644 --- a/api_docs/telemetry.mdx +++ b/api_docs/telemetry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetry title: "telemetry" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetry plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetry'] --- import telemetryObj from './telemetry.devdocs.json'; diff --git a/api_docs/telemetry_collection_manager.mdx b/api_docs/telemetry_collection_manager.mdx index 35577ec87cc3c..a175d612a4e7b 100644 --- a/api_docs/telemetry_collection_manager.mdx +++ b/api_docs/telemetry_collection_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager title: "telemetryCollectionManager" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionManager plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionManager'] --- import telemetryCollectionManagerObj from './telemetry_collection_manager.devdocs.json'; diff --git a/api_docs/telemetry_collection_xpack.mdx b/api_docs/telemetry_collection_xpack.mdx index db07655083e35..18a82f1b71edf 100644 --- a/api_docs/telemetry_collection_xpack.mdx +++ b/api_docs/telemetry_collection_xpack.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionXpack title: "telemetryCollectionXpack" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionXpack plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionXpack'] --- import telemetryCollectionXpackObj from './telemetry_collection_xpack.devdocs.json'; diff --git a/api_docs/telemetry_management_section.mdx b/api_docs/telemetry_management_section.mdx index 765d85612f920..79a860406a302 100644 --- a/api_docs/telemetry_management_section.mdx +++ b/api_docs/telemetry_management_section.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection title: "telemetryManagementSection" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryManagementSection plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/text_based_languages.mdx b/api_docs/text_based_languages.mdx index f01d9a90cd443..50dc9a5316727 100644 --- a/api_docs/text_based_languages.mdx +++ b/api_docs/text_based_languages.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/textBasedLanguages title: "textBasedLanguages" image: https://source.unsplash.com/400x175/?github description: API docs for the textBasedLanguages plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'textBasedLanguages'] --- import textBasedLanguagesObj from './text_based_languages.devdocs.json'; diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index f1f950e32747e..34394247d19ce 100644 --- a/api_docs/threat_intelligence.mdx +++ b/api_docs/threat_intelligence.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/threatIntelligence title: "threatIntelligence" image: https://source.unsplash.com/400x175/?github description: API docs for the threatIntelligence plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'threatIntelligence'] --- import threatIntelligenceObj from './threat_intelligence.devdocs.json'; diff --git a/api_docs/timelines.mdx b/api_docs/timelines.mdx index 2f36fbd5ebf85..b0de367357e96 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github description: API docs for the timelines plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'timelines'] --- import timelinesObj from './timelines.devdocs.json'; diff --git a/api_docs/transform.mdx b/api_docs/transform.mdx index 80a02b2c69b24..e7f9632f00861 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github description: API docs for the transform plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.devdocs.json b/api_docs/triggers_actions_ui.devdocs.json index d883772477845..cc371e2134497 100644 --- a/api_docs/triggers_actions_ui.devdocs.json +++ b/api_docs/triggers_actions_ui.devdocs.json @@ -5616,6 +5616,8 @@ "signature": [ "PreConfiguredActionConnector", " | ", + "SystemAction", + " | ", "UserConfiguredActionConnector", "" ], diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index e9e1c36ee918f..cbcb2011869cf 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github description: API docs for the triggersActionsUi plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-o | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 547 | 11 | 521 | 49 | +| 547 | 11 | 521 | 50 | ## Client diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index 1820485d397f6..cad6c4aca0a0d 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActions plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions'] --- import uiActionsObj from './ui_actions.devdocs.json'; diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx index daecbdd0d463a..2756ee9783a4f 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActionsEnhanced plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_histogram.mdx b/api_docs/unified_histogram.mdx index 26e8b03a31ceb..a937258c3abb8 100644 --- a/api_docs/unified_histogram.mdx +++ b/api_docs/unified_histogram.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedHistogram title: "unifiedHistogram" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedHistogram plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedHistogram'] --- import unifiedHistogramObj from './unified_histogram.devdocs.json'; diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 9ca7e0c48c855..0fee1d0bb852a 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch'] --- import unifiedSearchObj from './unified_search.devdocs.json'; diff --git a/api_docs/unified_search_autocomplete.mdx b/api_docs/unified_search_autocomplete.mdx index 44e0532cc60b4..4c7e6ec585f92 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch.autocomplete plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index 3eef30cddca4e..52ea956af6173 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github description: API docs for the urlForwarding plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'urlForwarding'] --- import urlForwardingObj from './url_forwarding.devdocs.json'; diff --git a/api_docs/usage_collection.mdx b/api_docs/usage_collection.mdx index 8fbcaf41c5412..28fda03e4c7b0 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the usageCollection plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'usageCollection'] --- import usageCollectionObj from './usage_collection.devdocs.json'; diff --git a/api_docs/ux.mdx b/api_docs/ux.mdx index 84718215e6148..35e4985760225 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github description: API docs for the ux plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ux'] --- import uxObj from './ux.devdocs.json'; diff --git a/api_docs/vis_default_editor.mdx b/api_docs/vis_default_editor.mdx index 3445683bb5db4..c46d34717bc97 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the visDefaultEditor plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visDefaultEditor'] --- import visDefaultEditorObj from './vis_default_editor.devdocs.json'; diff --git a/api_docs/vis_type_gauge.mdx b/api_docs/vis_type_gauge.mdx index 5252022e72401..963e602c35be5 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeGauge plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeGauge'] --- import visTypeGaugeObj from './vis_type_gauge.devdocs.json'; diff --git a/api_docs/vis_type_heatmap.mdx b/api_docs/vis_type_heatmap.mdx index 75e41b1380b80..23040c12a4a17 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeHeatmap plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeHeatmap'] --- import visTypeHeatmapObj from './vis_type_heatmap.devdocs.json'; diff --git a/api_docs/vis_type_pie.mdx b/api_docs/vis_type_pie.mdx index 70cb5ba73fa29..3ab7c3e7f5eb4 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypePie plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypePie'] --- import visTypePieObj from './vis_type_pie.devdocs.json'; diff --git a/api_docs/vis_type_table.mdx b/api_docs/vis_type_table.mdx index b677cc575d14b..7efbd34be21d7 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTable plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTable'] --- import visTypeTableObj from './vis_type_table.devdocs.json'; diff --git a/api_docs/vis_type_timelion.mdx b/api_docs/vis_type_timelion.mdx index 342f57da12f1b..74a5b9517cec4 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimelion plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimelion'] --- import visTypeTimelionObj from './vis_type_timelion.devdocs.json'; diff --git a/api_docs/vis_type_timeseries.mdx b/api_docs/vis_type_timeseries.mdx index ac3272a9bd92e..20d1d82c6f814 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimeseries plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimeseries'] --- import visTypeTimeseriesObj from './vis_type_timeseries.devdocs.json'; diff --git a/api_docs/vis_type_vega.mdx b/api_docs/vis_type_vega.mdx index d7373253c1ba2..3446f042583a1 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVega plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVega'] --- import visTypeVegaObj from './vis_type_vega.devdocs.json'; diff --git a/api_docs/vis_type_vislib.mdx b/api_docs/vis_type_vislib.mdx index 950bd297eb8be..937a47e7be8c4 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVislib plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVislib'] --- import visTypeVislibObj from './vis_type_vislib.devdocs.json'; diff --git a/api_docs/vis_type_xy.mdx b/api_docs/vis_type_xy.mdx index 375a42ed70b70..e4ab55019da23 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeXy plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualization_ui_components.mdx b/api_docs/visualization_ui_components.mdx index 835dfbe6bf1f4..4df3e47fce1b6 100644 --- a/api_docs/visualization_ui_components.mdx +++ b/api_docs/visualization_ui_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizationUiComponents title: "visualizationUiComponents" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizationUiComponents plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizationUiComponents'] --- import visualizationUiComponentsObj from './visualization_ui_components.devdocs.json'; diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index adcda94576e48..b208bb40e55e7 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizations plugin -date: 2023-06-30 +date: 2023-07-03 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; diff --git a/dev_docs/tutorials/debugging.mdx b/dev_docs/tutorials/debugging.mdx index fabd475f88d77..2f9818f571046 100644 --- a/dev_docs/tutorials/debugging.mdx +++ b/dev_docs/tutorials/debugging.mdx @@ -85,7 +85,7 @@ Backend (APM Node): In some cases, it is beneficial to enable APM on a local development environment to get an initial undesrtanding of a feature's performance during manual or automatic tests. 1. Create a secondary monitoring deployment to collect APM data. The easiest option is to use [Elastic Cloud](https://cloud.elastic.co/deployments) to create a new deployment. - 2. Open Kibana, go to `Integrations` and enable the Elastic APM integration. + 2. Open the Kibana from the monitoring deployment (_not_ your local Kibana), go to `Integrations` and enable the Elastic APM integration. 3. Scroll down and copy the server URL and secret token. You may also find them in your cloud console under APM & Fleet. 4. Create or open `config\kibana.dev.yml` on your local development environment. 5. Add the following settings: diff --git a/docs/CHANGELOG.asciidoc b/docs/CHANGELOG.asciidoc index e90fe1cbc9a1a..4685d40e5ddbf 100644 --- a/docs/CHANGELOG.asciidoc +++ b/docs/CHANGELOG.asciidoc @@ -47,8 +47,6 @@ Review important information about the {kib} 8.x releases. [[release-notes-8.8.2]] == {kib} 8.8.2 -coming::[8.8.2] - Review the following information about the {kib} 8.8.2 release. [float] diff --git a/docs/apm/agent-explorer.asciidoc b/docs/apm/agent-explorer.asciidoc new file mode 100644 index 0000000000000..2129853c9ee8c --- /dev/null +++ b/docs/apm/agent-explorer.asciidoc @@ -0,0 +1,18 @@ +[[agent-explorer]] +=== APM Agent explorer + +++++ +Identify deployment details for APM agents +++++ + +beta::[] + +APM agent explorer provides a centralized panel to identify APM agent deployment details, like service name, environment, instances, and agent name, version, and documentation. + +[role="screenshot"] +image::apm/images/apm-agent-explorer.png[APM agent explorer] + +Select an APM agent to expand it and view the details of each agent instance. + +[role="screenshot"] +image::apm/images/apm-agent-explorer-flyout.png[APM agent explorer flyout] diff --git a/docs/apm/apm-alerts.asciidoc b/docs/apm/apm-alerts.asciidoc index 102a31be4c4a9..cda045e8d8ccd 100644 --- a/docs/apm/apm-alerts.asciidoc +++ b/docs/apm/apm-alerts.asciidoc @@ -11,44 +11,52 @@ and trigger built-in **actions** when those conditions are met. The following **rules** are supported: -* Latency anomaly rule: -Alert when latency of a service is abnormal -* Transaction error rate threshold rule: -Alert when the service's transaction error rate is above the defined threshold -* Error count threshold rule: -Alert when the number of errors in a service exceeds a defined threshold +* **Threshold rule**: +Alert when the latency or failed transaction rate is abnormal. +Threshold rules can be as broad or as granular as you'd like, enabling you to define exactly when you want to be alerted--whether that's at the environment level, service name level, transaction type level, and/or transaction name level. +* **Anomaly rule**: +Alert when either the latency of a service is anomalous. Anomaly rules can be set at the environment level, service level, and/or transaction type level. +* **Error count rule**: +Alert when the number of errors in a service exceeds a defined threshold. Error count rules can be set at the environment level, service level, and error group level. [role="screenshot"] image::apm/images/apm-alert.png[Create an alert in the APM app] +Below, we'll walk through the creation of two APM rules. + For a complete walkthrough of the **Create rule** flyout panel, including detailed information on each configurable property, see Kibana's <>. -Below, we'll walk through the creation of two APM rules. - [float] [[apm-create-transaction-alert]] === Example: create a latency anomaly rule Latency anomaly rules trigger when the latency of a service is abnormal. +Because some parts of an application are more important than others, and have a different +tolerance for latency, we'll target a specific transaction within a service. + +Before continuing, identify the service name, transaction type, and environment that you'd like to create a latency anomaly rule for. This guide will create an alert for all services based on the following criteria: -* Environment: production +* Service: `{your_service.name}` +* Transaction: `{your_transaction.name}` +* Environment: `{your_service.environment}` * Severity level: critical -* Run every five minutes -* Send an alert to a Slack channel only when the rule status changes +* Check every five minutes +* Send an alert to a Slack channel when the rule status changes -From any page in the APM app, select **Alerts and rules** > **Latency** > **Create anomaly rule**. -Change the name of the alert, but do not edit the tags. +From any page in the APM app, select **Alerts and rules** > **Create anomaly rule**. +Change the name of the rule, but do not edit the tags. Based on the criteria above, define the following rule details: -* **Check every** - `5 minutes` -* **Notify** - "Only on status change" -* **Environment** - `all` +* **Service** - `{your_service.name}` +* **Type** - `{your_transaction.name}` +* **Environment** - `{your_service.environment}` * **Has anomaly with severity** - `critical` +* **Check every** - `5 minutes` -Next, add a connector. Multiple connectors can be selected, but in this example we're interested in Slack. +Next, add a connector type. Multiple connectors can be selected, but in this example we're interested in Slack. Select **Slack** > **Create a connector**. Enter a name for the connector, and paste your Slack webhook URL. @@ -60,30 +68,40 @@ to pass additional alert values at the time a condition is detected to an action A list of available variables can be accessed by selecting the **add variable** button image:apm/images/add-variable.png[add variable button]. -Click **Save**. The rule has been created and is now active! +Click **Save**. Your rule has been created and is now active! [float] [[apm-create-error-alert]] === Example: create an error count threshold alert The error count threshold alert triggers when the number of errors in a service exceeds a defined threshold. -This guide will create an alert for all services based on the following criteria: +Because some errors are more important than others, this guide will focus a specific error group ID. + +Before continuing, identify the service name, environment name, and error group ID that you'd like to create a latency anomaly rule for. +The easiest way to find an error group ID is to select the service that you're interested in and navigating to the **Errors** tab. -* All environments -* Error rate is above 25 for the last minute -* Check every 1 minute, and alert every time the rule is active +This guide will create an alert for an error group ID based on the following criteria: + +* Service: `{your_service.name}` +* Environment: `{your_service.environment}` +* Error Grouping Key: `{your_error.ID}` +* Error rate is above 25 errors for the last five minutes +* Group alerts by `service.name` and `service.environment` +* Check every 1 minute * Send the alert via email to the site reliability team -From any page in the APM app, select **Alerts and rules** > **Error count** > **Create threshold rule**. +From any page in the APM app, select **Alerts and rules** > **Create error count rule**. Change the name of the alert, but do not edit the tags. Based on the criteria above, define the following rule details: -* **Check every** - `1 minute` -* **Notify** - "Every time alert is active" -* **Environment** - `all` +* **Service**: `{your_service.name}` +* **Environment**: `{your_service.environment}` +* **Error Grouping Key**: `{your_error.ID}` * **Is above** - `25 errors` -* **For the last** - `1 minute` +* **For the last** - `5 minutes` +* **Group alerts by** - `service.name` `service.environment` +* **Check every** - `1 minute` Select the **Email** connector and click **Create a connector**. Fill out the required details: sender, host, port, etc., and click **save**. @@ -96,6 +114,32 @@ A list of available variables can be accessed by selecting the Click **Save**. The alert has been created and is now active! +[float] +[[apm-alert-view-active]] +=== View active alerts + +Active alerts are displayed and grouped in multiple ways in the APM app. + +[float] +[[apm-alert-view-group]] +==== View alerts by service group + +If you're using the <> feature, you can view alerts by service group. +From the service group overview page, click the red alert indicator to open the **Alerts** tab with a predefined filter that matches the filter used when creating the service group. + +[role="screenshot"] +image::apm/images/apm-service-group.png[Example view of service group in the APM app in Kibana] + +[float] +[[apm-alert-view-service]] +==== View alerts by service + +Alerts can be viewed within the context of any service. +After selecting a service, go to the **Alerts** tab to view any alerts that are active for the selected service. + +[role="screenshot"] +image::apm/images/active-alert-service.png[View active alerts by service] + [float] [[apm-alert-manage]] === Manage alerts and rules diff --git a/docs/apm/dependencies.asciidoc b/docs/apm/dependencies.asciidoc index b3afdc4880df5..4ac5cdef3f71c 100644 --- a/docs/apm/dependencies.asciidoc +++ b/docs/apm/dependencies.asciidoc @@ -30,3 +30,19 @@ If your usage pattern _has_ changed, the dependency view can quickly show you wh that pattern change exists in all upstream services, or just a subset of your services. You might then start digging into traces coming from impacted services to determine why that pattern change has occurred. + +[float] +[[dependencies-operations]] +==== Operations + +beta::[] + +**Dependency operations** provides a granular breakdown of the operations/queries a dependency is executing. + +[role="screenshot"] +image::apm/images/operations.png[operations view in the APM app in Kibana] + +Selecting an operation displays the operation's impact and performance trends over time, via key metrics like latency, throughput, and failed transaction rate. In addition, the <> provides a visual drill-down into an end-to-end trace sample. + +[role="screenshot"] +image::apm/images/operations-detail.png[operations detail view in the APM app in Kibana] diff --git a/docs/apm/getting-started.asciidoc b/docs/apm/getting-started.asciidoc index 22830a845e8ed..1a369202788e1 100644 --- a/docs/apm/getting-started.asciidoc +++ b/docs/apm/getting-started.asciidoc @@ -40,6 +40,8 @@ Notice something awry? Select a service or trace and dive deeper with: * <> * <> * <> +* <> +* <> TIP: Want to learn more about the Elastic APM ecosystem? See the {apm-guide-ref}/apm-overview.html[APM Overview]. @@ -63,3 +65,7 @@ include::spans.asciidoc[] include::errors.asciidoc[] include::metrics.asciidoc[] + +include::infrastructure.asciidoc[] + +include::logs.asciidoc[] \ No newline at end of file diff --git a/docs/apm/how-to-guides.asciidoc b/docs/apm/how-to-guides.asciidoc index 4f702adf01a22..1f1670a12e6fd 100644 --- a/docs/apm/how-to-guides.asciidoc +++ b/docs/apm/how-to-guides.asciidoc @@ -11,6 +11,7 @@ Learn how to perform common APM app tasks. * <> * <> * <> +* <> * <> * <> * <> @@ -30,6 +31,8 @@ include::filters.asciidoc[] include::correlations.asciidoc[] +include::agent-explorer.asciidoc[] + include::machine-learning.asciidoc[] include::lambda.asciidoc[] diff --git a/docs/apm/images/active-alert-service.png b/docs/apm/images/active-alert-service.png new file mode 100644 index 0000000000000..7d7deaf078ffb Binary files /dev/null and b/docs/apm/images/active-alert-service.png differ diff --git a/docs/apm/images/apm-agent-explorer-flyout.png b/docs/apm/images/apm-agent-explorer-flyout.png new file mode 100644 index 0000000000000..9792a93a71234 Binary files /dev/null and b/docs/apm/images/apm-agent-explorer-flyout.png differ diff --git a/docs/apm/images/apm-agent-explorer.png b/docs/apm/images/apm-agent-explorer.png new file mode 100644 index 0000000000000..c6fb7d031ed92 Binary files /dev/null and b/docs/apm/images/apm-agent-explorer.png differ diff --git a/docs/apm/images/apm-alert.png b/docs/apm/images/apm-alert.png index a845d65dd24a5..92b6f5dde9730 100644 Binary files a/docs/apm/images/apm-alert.png and b/docs/apm/images/apm-alert.png differ diff --git a/docs/apm/images/apm-query-bar.png b/docs/apm/images/apm-query-bar.png index a1fb129d3c200..457573f485f5a 100644 Binary files a/docs/apm/images/apm-query-bar.png and b/docs/apm/images/apm-query-bar.png differ diff --git a/docs/apm/images/apm-services-overview.png b/docs/apm/images/apm-services-overview.png index 97c46cb756b28..0badeea3be4b3 100644 Binary files a/docs/apm/images/apm-services-overview.png and b/docs/apm/images/apm-services-overview.png differ diff --git a/docs/apm/images/apm-traces.png b/docs/apm/images/apm-traces.png index ee16f9ed16a18..c8b8d40b01335 100644 Binary files a/docs/apm/images/apm-traces.png and b/docs/apm/images/apm-traces.png differ diff --git a/docs/apm/images/dependencies-drilldown.png b/docs/apm/images/dependencies-drilldown.png index 4c491c1ffa254..af82ee3d9305f 100644 Binary files a/docs/apm/images/dependencies-drilldown.png and b/docs/apm/images/dependencies-drilldown.png differ diff --git a/docs/apm/images/infra.png b/docs/apm/images/infra.png new file mode 100644 index 0000000000000..e139012270d5f Binary files /dev/null and b/docs/apm/images/infra.png differ diff --git a/docs/apm/images/lambda-cold-start.png b/docs/apm/images/lambda-cold-start.png deleted file mode 100644 index 54c3d36fddc64..0000000000000 Binary files a/docs/apm/images/lambda-cold-start.png and /dev/null differ diff --git a/docs/apm/images/lambda-overview.png b/docs/apm/images/lambda-overview.png new file mode 100644 index 0000000000000..9d0558949f0ce Binary files /dev/null and b/docs/apm/images/lambda-overview.png differ diff --git a/docs/apm/images/logs.png b/docs/apm/images/logs.png new file mode 100644 index 0000000000000..94d77b47495f1 Binary files /dev/null and b/docs/apm/images/logs.png differ diff --git a/docs/apm/images/operations-detail.png b/docs/apm/images/operations-detail.png new file mode 100644 index 0000000000000..64a1c6550859d Binary files /dev/null and b/docs/apm/images/operations-detail.png differ diff --git a/docs/apm/images/operations.png b/docs/apm/images/operations.png new file mode 100644 index 0000000000000..119f8bdf99ff7 Binary files /dev/null and b/docs/apm/images/operations.png differ diff --git a/docs/apm/images/storage-explorer-overview.png b/docs/apm/images/storage-explorer-overview.png index c9c02b4bbdb7a..317ad02d82e43 100644 Binary files a/docs/apm/images/storage-explorer-overview.png and b/docs/apm/images/storage-explorer-overview.png differ diff --git a/docs/apm/images/trace-explorer.png b/docs/apm/images/trace-explorer.png new file mode 100644 index 0000000000000..70c13f650e2fb Binary files /dev/null and b/docs/apm/images/trace-explorer.png differ diff --git a/docs/apm/infrastructure.asciidoc b/docs/apm/infrastructure.asciidoc new file mode 100644 index 0000000000000..f9cf45aa490c2 --- /dev/null +++ b/docs/apm/infrastructure.asciidoc @@ -0,0 +1,13 @@ +[role="xpack"] +[[infrastructure]] +=== Infrastructure + +The *Infrastructure* tab provides information about the containers, pods, and hosts, +that the selected service is linked to. + +[role="screenshot"] +image::apm/images/infra.png[Example view of the Infrastructure tab in APM app in Kibana] + +IT ops and software reliability engineers (SREs) can use this tab +to quickly find a service's underlying infrastructure resources when debugging a problem. +Knowing what infrastructure is related to a service allows you to remediate issues by restarting, killing hanging instances, changing configuration, rolling back deployments, scaling up, scaling out, etc. \ No newline at end of file diff --git a/docs/apm/lambda.asciidoc b/docs/apm/lambda.asciidoc index 2dcdf5b462b63..af3012dd19a5e 100644 --- a/docs/apm/lambda.asciidoc +++ b/docs/apm/lambda.asciidoc @@ -3,11 +3,15 @@ === Observe Lambda functions Elastic APM provides performance and error monitoring for AWS Lambda functions. -Get insight into function execution and runtime behavior, as well as visibility into how your Lambda functions relate to and depend on other services. +See how your Lambda functions relate to and depend on other services, and +get insight into function execution and runtime behavior, like lambda duration, cold start rate, cold start duration, compute usage, memory usage, and more. To set up Lambda monitoring, see the relevant {apm-guide-ref}/monitoring-aws-lambda.html[quick start guide]. +[role="screenshot"] +image::apm/images/lambda-overview.png[lambda overview] + [float] [[apm-lambda-cold-start-info]] ==== Cold starts @@ -22,9 +26,6 @@ Cold starts are an unavoidable byproduct of the serverless world, but visibility The cold start rate (i.e. proportion of requests that experience a cold start) is displayed per service and per transaction. -[role="screenshot"] -image::apm/images/lambda-cold-start.png[lambda cold start graph] - Cold start is also displayed in the trace waterfall, where you can drill-down into individual traces and see trace metadata like AWS request ID, trigger type, and trigger request ID. [role="screenshot"] diff --git a/docs/apm/logs.asciidoc b/docs/apm/logs.asciidoc new file mode 100644 index 0000000000000..04f5eff185692 --- /dev/null +++ b/docs/apm/logs.asciidoc @@ -0,0 +1,19 @@ +[role="xpack"] +[[logs]] +=== Logs + +The *Logs* tab shows contextual logs for the selected service. + +// tag::log-overview[] +Logs provide detailed information about specific events, and are crucial to successfully debugging slow or erroneous transactions. + +If you've correlated your application's logs and traces, you never have to search for relevant data; it's already available to you. Viewing log and trace data together allows you to quickly diagnose and solve problems. + +To learn how to correlate your logs with your instrumented services, +see {observability-guide}/application-logs.html[log correlation] +// end::log-overview[] + +[role="screenshot"] +image::apm/images/logs.png[Example view of the Logs tab in APM app in Kibana] + +TIP: Logs displayed on this page are filtered on `service.name` diff --git a/docs/apm/service-overview.asciidoc b/docs/apm/service-overview.asciidoc index 1311aa84fca18..a47ba33a7486f 100644 --- a/docs/apm/service-overview.asciidoc +++ b/docs/apm/service-overview.asciidoc @@ -139,9 +139,6 @@ or when to remove a large dependency. The cold start rate chart is currently supported for <> functions and Azure functions. -[role="screenshot"] -image::apm/images/lambda-cold-start.png[lambda cold start graph] - [discrete] [[service-instances]] === Instances diff --git a/docs/apm/services.asciidoc b/docs/apm/services.asciidoc index 7009aac04ffa8..7ce3354ecbf7e 100644 --- a/docs/apm/services.asciidoc +++ b/docs/apm/services.asciidoc @@ -10,6 +10,8 @@ To help surface potential issues, services are sorted by their health status: Health status is powered by <> and requires anomaly detection to be enabled. +In addition to health status, active alerts for each service are prominently displayed in the service inventory table. Selecting an active alert badge brings you to the <> tab where you can learn more about the active alert and take action. + [role="screenshot"] image::apm/images/apm-services-overview.png[Example view of services table the APM app in Kibana] @@ -17,11 +19,14 @@ image::apm/images/apm-services-overview.png[Example view of services table the A [[service-groups]] ==== Service groups -preview::[] +beta::[] -Group services together to build meaningful views that remove noise and simplify investigations across services. +Group services together to build meaningful views that remove noise, simplify investigations across services, +and <>. Service groups are {kib} space-specific and available for any users with appropriate access. +// This screenshot is reused in the alerts docs +// Ensure it has an active alert showing [role="screenshot"] image::apm/images/apm-service-group.png[Example view of service group in the APM app in Kibana] diff --git a/docs/apm/settings.asciidoc b/docs/apm/settings.asciidoc index 44da63143f63f..c11ab5f70f6dd 100644 --- a/docs/apm/settings.asciidoc +++ b/docs/apm/settings.asciidoc @@ -21,3 +21,14 @@ include::./../settings/apm-settings.asciidoc[tag=apm-indices-settings] === General APM settings include::./../settings/apm-settings.asciidoc[tag=general-apm-settings] + +[float] +[[apm-labs]] +=== APM Labs + +**APM Labs** allows you to easily try out new features that are technical preview. + +To enable APM labs, navigate to **APM** > **Settings** > **General settings** and toggle **Enable labs button in APM**. +Select **Save changes** and refresh the page. + +After enabling **APM Labs** select **Labs** in the toolbar to see the technical preview features available to try out. diff --git a/docs/apm/traces.asciidoc b/docs/apm/traces.asciidoc index 971d358092489..a3bd707b60629 100644 --- a/docs/apm/traces.asciidoc +++ b/docs/apm/traces.asciidoc @@ -19,3 +19,18 @@ If there's a particular endpoint you're worried about, select it to view its [role="screenshot"] image::apm/images/apm-traces.png[Example view of the Traces overview in APM app in Kibana] + +[float] +[[trace-explorer]] +==== Trace explorer + +preview::[] + +**Trace explorer** is an experimental top-level search tool that allows you to query your traces using <> or {ref}/eql.html[Event Query Language (EQL)]. + +Curate your own custom queries, or use the <> to find and select edges to automatically generate queries based on your selection: + +[role="screenshot"] +image::apm/images/trace-explorer.png[Trace explorer] + +Enable **Trace explorer** in <> or in <>. diff --git a/docs/apm/transactions.asciidoc b/docs/apm/transactions.asciidoc index 307213e44fa6e..b0a1c9ee858de 100644 --- a/docs/apm/transactions.asciidoc +++ b/docs/apm/transactions.asciidoc @@ -162,12 +162,7 @@ This means you can select "Actions - View transaction in Discover" to see the ac The *Logs* tab displays logs related to the sampled trace. -Logs provide detailed information about specific events, -and are crucial to successfully debugging slow or erroneous transactions. - -If you've correlated your application's logs and traces, you never have to search for relevant data; -it's all provided on this. Viewing log and trace data together allows you to quickly diagnose -and solve problems. +include::./logs.asciidoc[tag=log-overview] [role="screenshot"] image::apm/images/apm-logs-tab.png[APM logs tab] diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index cb51a6779b604..936520046c5f1 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -393,9 +393,20 @@ value is 10000. [[apm-enable-service-overview]]`apm:enableServiceOverview`:: When enabled, displays the *Overview* tab for services in *APM*. +[[observability-apm-critical-path]]`observability:apmEnableCriticalPath`:: +When enabled, displays the critical path of a trace. + +[[observability-enable-progressive-loading]]`observability:apmProgressiveLoading`:: +preview:[] When enabled, uses progressive loading of some APM views. +Data may be requested with a lower sampling rate first, with lower accuracy but faster response times, +while the unsampled data loads in the background. + [[observability-apm-optimized-sort]]`observability:apmServiceInventoryOptimizedSorting`:: preview:[] Sorts services without anomaly detection rules on the APM Service inventory page by service name. +[[observability-enable-aws-lambda-metrics]]`observability:enableAwsLambdaMetrics`:: +preview:[] Display Amazon Lambda metrics in the service metrics tab. + [[observability-apm-enable-comparison]]`observability:enableComparisonByDefault`:: Enables the comparison feature in the APM app. @@ -408,6 +419,9 @@ When enabled, allows you to inspect {es} queries in API responses. [[observability-apm-enable-service-groups]]`observability:enableServiceGroups`:: preview:[] When enabled, allows users to create Service Groups from the APM Service Inventory page. +[[observability-apm-trace-explorer-tab]]`observability:apmTraceExplorerTab`:: +preview:[] Enable the APM Trace Explorer feature, that allows you to search and inspect traces with KQL or EQL. + [float] [[kibana-reporting-settings]] ==== Reporting diff --git a/examples/v8_profiler_examples/README.md b/examples/v8_profiler_examples/README.md new file mode 100644 index 0000000000000..50c188ab0e283 --- /dev/null +++ b/examples/v8_profiler_examples/README.md @@ -0,0 +1,112 @@ +# V8 profiler examples + +Provides access to the V8 CPU Profiler and Heap Profiler, to inspect +the Kibana running this plugin. + +The endpoints are: + + /_dev/cpu_profile?duration=(seconds)&interval=(microseconds) + /_dev/heap_profile?duration=(seconds)&interval=(bytes) + +The default duration is 5 seconds. + +For more information on these V8 APIs, see: + +- https://chromedevtools.github.io/devtools-protocol/v8/Profiler/ +- https://chromedevtools.github.io/devtools-protocol/v8/HeapProfiler/ + +Note that due to bugs or limitations, +it's not possible to generate heap snapshots using the techniques used +to generate the cpu and heap profiles. + +Try them right now, assuming you started Kibana with `--run-examples`! + +- [`http://localhost:5601/_dev/cpu_profile`](http://localhost:5601/_dev/cpu_profile) +- [`http://localhost:5601/_dev/heap_profile`](http://localhost:5601/_dev/heap_profile) + + +When using curl, you can use the `-kOJ` options, which: + +- `-k --insecure`: allow HTTPS usage with self-signed certs +- `-O --remote-name`: use the server-specified name for this download +- `-J --remote-header-name`: use the `Content-Disposition` as the name of + the download + +So one of the following should work for you, to run a 10 second CPU profile +using an interval of 100μs (default: 5s / 1000μs): + +``` +curl -OJ "http://elastic:changeme@localhost:5601/_dev/cpu_profile?duration=10&interval=100" +curl -kOJ "https://elastic:changeme@localhost:5601/_dev/cpu_profile?duration=10&interval=100" +``` + +The files generated will be: + + MM-DD_hh-mm-ss.cpuprofile + MM-DD_hh-mm-ss.heapprofile + +These filetypes are the ones expected by various V8 tools that can read these. + +You can use these URLs in your browser, and the files will be saved with the +generated names. + +## profile / heap profile readers + +The traditional tools used to view these are part of Chrome Dev Tools (CDT) +and now VS Code also supports viewing these files. They provide +similar capabilities. + +There doesn't seem to be a much doc available on how to use the viewers +for these files. The Chrome Dev Tools docs are extremely old and appear +to be out-of-date with the current user interface. The VS Code +documentation [Analyzing a profile][] is more recent, but there's not +much there. + +[Analyzing a profile]: https://code.visualstudio.com/docs/nodejs/profiling#_analyzing-a-profile + +For CPU profiles, open CDT and then click on the "Performance" tab. You +should be able to drop a file right from Finder / Explorer onto the CDT +window, and then get the visualization of the profile. If you +downloaded the profile right from the browser, using the URL in the URL +bar, you can drop the download file from the download status button +right into CDT. + +For heap profiles, open CDT and then click on the "Memory" tab. Drag and +drop doesn't seem to work here, but you can load a file via a file +prompter by clicking the "Load" button at the bottom of the "Memory" +pane. You will probably need to scroll to see the button. + +VSCode now supports `.cpuprofile` files and `.heapprofile` files +directly, displaying them as a table of function timings. There is also +[an extension available to display flame charts][] installed by clicking +on the grey-ed out "flame" button on the top-right of the cpu profile +view. + +[an extension available to display flame charts]: https://marketplace.visualstudio.com/items?itemName=ms-vscode.vscode-js-profile-flame + +There seem to be problems with both CDT and the VS Code tools, at times. +The flame charts in VS Code seem to go haywire sometimes. The heap +profile tables in CDT don't expand. Etc. So, beware, and be prepared to +have to use multiple tools to analyze these files. + +An alternate view of CPU profiles, which organizes files based on +"packages", is available with the **NO**de **PRO**filer (`no-pro`) thing +available at https://pmuellr.github.io/no-pro/ . It also supports +drag-n-drop of CPU profile files. Note that you can get more +directories to show up as "packages", by bringing up CDT and running the +following code: + + localStorage['fake-packages-dirs'] = "x-pack/plugins,packages,src/core" + + +## tips / tricks + +If you're handy with Mac Finder, or other ways of auto-launching apps +based on file extensions, it's easy to associate `.cpuprofile` files +with vscode. + +Since the http endpoints are GET requests, they are easy to bookmark. +Start a profile by clicking on a bookmark. + +When these files are downloaded via Chrome, you can typically launch +them directly from the download bar, or drag the file to a viewer. diff --git a/examples/v8_profiler_examples/kibana.jsonc b/examples/v8_profiler_examples/kibana.jsonc new file mode 100644 index 0000000000000..a011bd751ce8c --- /dev/null +++ b/examples/v8_profiler_examples/kibana.jsonc @@ -0,0 +1,12 @@ +{ + "type": "plugin", + "id": "@kbn/v8-profiler-examples-plugin", + "owner": "@elastic/response-ops", + "description": "Provides access to the v8 cpu profiler and heap profiler running this app", + "plugin": { + // umm, vs code says I can't use digits in the id field? + "id": "vEIGHTProfilerExamples", + "server": true, + "browser": false, + } +} diff --git a/packages/core/chrome/core-chrome-browser-internal/src/constants.ts b/examples/v8_profiler_examples/server/index.ts similarity index 52% rename from packages/core/chrome/core-chrome-browser-internal/src/constants.ts rename to examples/v8_profiler_examples/server/index.ts index 7c980bec2b2d8..d06809f7411b5 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/constants.ts +++ b/examples/v8_profiler_examples/server/index.ts @@ -6,8 +6,9 @@ * Side Public License, v 1. */ -export const KIBANA_FEEDBACK_LINK = - 'https://www.elastic.co/products/kibana/feedback?blade=kibanafeedback'; -export const KIBANA_ASK_ELASTIC_LINK = - 'https://www.elastic.co/products/kibana/ask-elastic?blade=kibanaaskelastic'; -export const GITHUB_CREATE_ISSUE_LINK = 'https://github.com/elastic/kibana/issues/new/choose'; +import { PluginInitializerContext } from '@kbn/core/server'; +import { V8ProfilerExamplesPlugin } from './plugin'; + +export function plugin(initializerContext: PluginInitializerContext) { + return new V8ProfilerExamplesPlugin(initializerContext); +} diff --git a/examples/v8_profiler_examples/server/lib/cpu_profile.ts b/examples/v8_profiler_examples/server/lib/cpu_profile.ts new file mode 100644 index 0000000000000..358f8c7b8bbba --- /dev/null +++ b/examples/v8_profiler_examples/server/lib/cpu_profile.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 { Session } from './session'; + +interface StartProfilingArgs { + interval: number; +} + +// Start a new profile, resolves to a function to stop the profile and resolve +// the profile data. +export async function startProfiling( + session: Session, + args: StartProfilingArgs +): Promise<() => any> { + session.logger.info(`starting cpu profile with args: ${JSON.stringify(args)}`); + + await session.post('Profiler.enable'); + // microseconds, v8 default is 1000 + await session.post('Profiler.setSamplingInterval', args); + await session.post('Profiler.start'); + + // returned function which stops the profile and resolves to the profile data + return async function stopProfiling() { + session.logger.info('stopping cpu profile'); + const result: any = await session.post('Profiler.stop'); + return result.profile; + }; +} diff --git a/examples/v8_profiler_examples/server/lib/deferred.ts b/examples/v8_profiler_examples/server/lib/deferred.ts new file mode 100644 index 0000000000000..9885b24a1691f --- /dev/null +++ b/examples/v8_profiler_examples/server/lib/deferred.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export function createDeferred() { + let resolver: any; + let rejecter: any; + + function resolve(...args: any[]) { + resolver(...args); + } + + function reject(...args: any[]) { + rejecter(...args); + } + + const promise = new Promise((resolve_, reject_) => { + resolver = resolve_; + rejecter = reject_; + }); + + return { promise, resolve, reject }; +} diff --git a/examples/v8_profiler_examples/server/lib/heap_profile.ts b/examples/v8_profiler_examples/server/lib/heap_profile.ts new file mode 100644 index 0000000000000..16e0e160476fe --- /dev/null +++ b/examples/v8_profiler_examples/server/lib/heap_profile.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Session } from './session'; + +interface StartProfilingArgs { + samplingInterval: number; + includeObjectsCollectedByMajorGC: boolean; + includeObjectsCollectedByMinorGC: boolean; +} + +// Start a new profile, resolves to a function to stop the profile and resolve +// the profile data. +export async function startProfiling( + session: Session, + args: StartProfilingArgs +): Promise<() => any> { + session.logger.info(`starting heap profile with args: ${JSON.stringify(args)}`); + + await session.post('Profiler.enable'); + await session.post('HeapProfiler.enable'); + await session.post('HeapProfiler.startSampling', args); + + // returned function which stops the profile and resolves to the profile data + return async function stopProfiling() { + session.logger.info('stopping heap profile'); + const result: any = await session.post('HeapProfiler.stopSampling'); + return result.profile; + }; +} diff --git a/examples/v8_profiler_examples/server/lib/session.ts b/examples/v8_profiler_examples/server/lib/session.ts new file mode 100644 index 0000000000000..6da96e7d6a8b8 --- /dev/null +++ b/examples/v8_profiler_examples/server/lib/session.ts @@ -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 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 { Logger } from '@kbn/core/server'; + +import { createDeferred } from './deferred'; + +let inspector: any = null; +try { + inspector = require('inspector'); +} catch (err) { + // inspector will be null :-( +} + +export async function createSession(logger: Logger): Promise { + logger.debug('creating session'); + + if (inspector == null) { + throw new Error('the inspector module is not available for this version of node'); + } + + let session = null; + try { + session = new inspector.Session(); + } catch (err) { + throw new Error(`error creating inspector session: ${err.message}`); + } + + try { + session.connect(); + } catch (err) { + throw new Error(`error connecting inspector session: ${err.message}`); + } + + return new Session(logger, session); +} + +export class Session { + readonly logger: Logger; + private session: any; + + constructor(logger: Logger, session: any) { + this.logger = logger; + this.session = session; + } + + async destroy() { + this.session.disconnect(); + this.session = null; + } + + on(event: string, handler: any) { + this.session.on(event, handler); + } + + async post(method: string, args?: any) { + this.logger.debug(`posting method ${method} ${JSON.stringify(args)}`); + if (this.session == null) { + throw new Error('session disconnected'); + } + + const deferred = createDeferred(); + + this.session.post(method, args, (err: any, response: any) => { + if (err) { + this.logger.debug(`error from method ${method}: ${err.message}`); + return deferred.reject(err); + } + deferred.resolve(response); + }); + + return deferred.promise; + } +} diff --git a/examples/v8_profiler_examples/server/plugin.ts b/examples/v8_profiler_examples/server/plugin.ts new file mode 100644 index 0000000000000..4ec29cf19ac0c --- /dev/null +++ b/examples/v8_profiler_examples/server/plugin.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. + */ + +import { Plugin, Logger, CoreSetup, PluginInitializerContext } from '@kbn/core/server'; + +import { registerRoutes } from './routes'; + +// this plugin's dependencies +export class V8ProfilerExamplesPlugin implements Plugin { + readonly logger: Logger; + + constructor(initializerContext: PluginInitializerContext) { + this.logger = initializerContext.logger.get(); + } + + public setup(core: CoreSetup) { + const router = core.http.createRouter(); + registerRoutes(this.logger, router); + } + + public start() {} + + public stop() {} +} diff --git a/examples/v8_profiler_examples/server/routes/common.ts b/examples/v8_profiler_examples/server/routes/common.ts new file mode 100644 index 0000000000000..f7b753a372846 --- /dev/null +++ b/examples/v8_profiler_examples/server/routes/common.ts @@ -0,0 +1,84 @@ +/* + * Copyright 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 { Logger, IKibanaResponse, KibanaResponseFactory } from '@kbn/core/server'; +import { createSession, Session } from '../lib/session'; +import { createDeferred } from '../lib/deferred'; + +type StopProfilingFn = () => Promise; +type StartProfilingFn = (session: Session, args: ArgType) => Promise; + +export async function handleRoute( + startProfiling: StartProfilingFn, + args: ArgType, + logger: Logger, + response: KibanaResponseFactory, + duration: number, + type: string +): Promise { + let session: Session; + + try { + session = await createSession(logger); + } catch (err) { + const message = `unable to create session: ${err.message}`; + logger.error(message); + return response.badRequest({ body: message }); + } + + const deferred = createDeferred(); + let stopProfiling: any; + try { + stopProfiling = await startProfiling(session, args); + } catch (err) { + const message = `unable to start ${type} profiling: ${err.message}`; + logger.error(message); + return response.badRequest({ body: message }); + } + + setTimeout(whenDone, 1000 * duration); + + let profile; + async function whenDone() { + try { + profile = await stopProfiling(); + } catch (err) { + logger.warn(`unable to capture ${type} profile: ${err.message}`); + } + deferred.resolve(); + } + + await deferred.promise; + + try { + await session.destroy(); + } catch (err) { + logger.warn(`unable to destroy session: ${err.message}`); + } + + if (profile == null) { + const message = `unable to capture ${type} profile`; + logger.error(message); + return response.badRequest({ body: message }); + } + + const fileName = new Date() + .toISOString() + .replace('T', '_') + .replace(/\//g, '-') + .replace(/:/g, '-') + .substring(5, 19); + + return response.ok({ + body: profile, + headers: { + 'Content-Type': 'application/octet-stream', + 'Content-Disposition': `attachment; filename="${fileName}.${type}profile"`, + }, + }); +} diff --git a/examples/v8_profiler_examples/server/routes/cpu_profile.ts b/examples/v8_profiler_examples/server/routes/cpu_profile.ts new file mode 100644 index 0000000000000..35eaff9e35adc --- /dev/null +++ b/examples/v8_profiler_examples/server/routes/cpu_profile.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { schema } from '@kbn/config-schema'; +import { Logger, IRouter } from '@kbn/core/server'; +import { startProfiling } from '../lib/cpu_profile'; +import { handleRoute } from './common'; + +const routeValidation = { + query: schema.object({ + // seconds to run the profile + duration: schema.number({ defaultValue: 5 }), + // microseconds, v8 default is 1000 + interval: schema.number({ defaultValue: 1000 }), + }), +}; + +const routeConfig = { + path: '/_dev/cpu_profile', + validate: routeValidation, +}; + +export function registerRoute(logger: Logger, router: IRouter): void { + router.get(routeConfig, async (context, request, response) => { + const { duration, interval } = request.query; + const args = { + interval, + }; + + return await handleRoute(startProfiling, args, logger, response, duration, 'cpu'); + }); +} diff --git a/examples/v8_profiler_examples/server/routes/heap_profile.ts b/examples/v8_profiler_examples/server/routes/heap_profile.ts new file mode 100644 index 0000000000000..9f5cbcff944c6 --- /dev/null +++ b/examples/v8_profiler_examples/server/routes/heap_profile.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 { schema } from '@kbn/config-schema'; +import { Logger, IRouter } from '@kbn/core/server'; +import { startProfiling } from '../lib/heap_profile'; +import { handleRoute } from './common'; + +const routeValidation = { + query: schema.object({ + // seconds to run the profile + duration: schema.number({ defaultValue: 5 }), + // Average sample interval in bytes. The default value is 32768 bytes. + interval: schema.number({ defaultValue: 32768 }), + includeMajorGC: schema.boolean({ defaultValue: true }), + includeMinorGC: schema.boolean({ defaultValue: true }), + }), +}; + +const routeConfig = { + path: '/_dev/heap_profile', + validate: routeValidation, +}; + +export function registerRoute(logger: Logger, router: IRouter): void { + router.get(routeConfig, async (context, request, response) => { + const { duration, interval, includeMajorGC, includeMinorGC } = request.query; + const args = { + samplingInterval: interval, + includeObjectsCollectedByMajorGC: includeMajorGC, + includeObjectsCollectedByMinorGC: includeMinorGC, + }; + + return await handleRoute(startProfiling, args, logger, response, duration, 'heap'); + }); +} diff --git a/examples/v8_profiler_examples/server/routes/index.ts b/examples/v8_profiler_examples/server/routes/index.ts new file mode 100644 index 0000000000000..fbe442eebad32 --- /dev/null +++ b/examples/v8_profiler_examples/server/routes/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. + */ + +import { Logger, IRouter } from '@kbn/core/server'; + +import { registerRoute as registerRouteCpuProfile } from './cpu_profile'; +import { registerRoute as registerRouteHeapProfile } from './heap_profile'; + +export function registerRoutes(logger: Logger, router: IRouter): void { + registerRouteCpuProfile(logger, router); + registerRouteHeapProfile(logger, router); +} diff --git a/examples/v8_profiler_examples/tsconfig.json b/examples/v8_profiler_examples/tsconfig.json new file mode 100644 index 0000000000000..31f0888931bcc --- /dev/null +++ b/examples/v8_profiler_examples/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types" + }, + "include": [ + "index.ts", + "server/**/*.ts", + "../../typings/**/*" + ], + "exclude": [ + "target/**/*", + ], + "kbn_references": [ + "@kbn/core", + "@kbn/config-schema", + ] +} diff --git a/fleet_packages.json b/fleet_packages.json index 208d8f079abc8..e35f69b4dbc17 100644 --- a/fleet_packages.json +++ b/fleet_packages.json @@ -58,6 +58,6 @@ }, { "name": "security_detection_engine", - "version": "8.8.6" + "version": "8.9.1" } ] \ No newline at end of file diff --git a/package.json b/package.json index d1c7fd0f769ff..9a3366b65ee29 100644 --- a/package.json +++ b/package.json @@ -555,6 +555,7 @@ "@kbn/rison": "link:packages/kbn-rison", "@kbn/rollup-plugin": "link:x-pack/plugins/rollup", "@kbn/routing-example-plugin": "link:examples/routing_example", + "@kbn/rrule": "link:packages/kbn-rrule", "@kbn/rule-data-utils": "link:packages/kbn-rule-data-utils", "@kbn/rule-registry-plugin": "link:x-pack/plugins/rule_registry", "@kbn/runtime-fields-plugin": "link:x-pack/plugins/runtime_fields", @@ -731,6 +732,7 @@ "@kbn/utility-types-jest": "link:packages/kbn-utility-types-jest", "@kbn/utils": "link:packages/kbn-utils", "@kbn/ux-plugin": "link:x-pack/plugins/ux", + "@kbn/v8-profiler-examples-plugin": "link:examples/v8_profiler_examples", "@kbn/vis-default-editor-plugin": "link:src/plugins/vis_default_editor", "@kbn/vis-type-gauge-plugin": "link:src/plugins/vis_types/gauge", "@kbn/vis-type-heatmap-plugin": "link:src/plugins/vis_types/heatmap", @@ -964,7 +966,6 @@ "require-in-the-middle": "^6.0.0", "reselect": "^4.1.6", "rison-node": "1.0.2", - "rrule": "2.6.4", "rxjs": "^7.5.5", "safe-squel": "^5.12.5", "seedrandom": "^3.0.5", diff --git a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx index 151ba56ad4aa0..65c7df00d39de 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/chrome_service.tsx @@ -15,7 +15,7 @@ import { EuiLink } from '@elastic/eui'; import useObservable from 'react-use/lib/useObservable'; import type { InternalInjectedMetadataStart } from '@kbn/core-injected-metadata-browser-internal'; import type { AnalyticsServiceSetup } from '@kbn/core-analytics-browser'; -import type { DocLinksStart } from '@kbn/core-doc-links-browser'; +import { type DocLinksStart } from '@kbn/core-doc-links-browser'; import type { HttpStart } from '@kbn/core-http-browser'; import { mountReactNode } from '@kbn/core-mount-utils-browser-internal'; import type { NotificationsStart } from '@kbn/core-notifications-browser'; @@ -33,8 +33,11 @@ import type { ChromeSetProjectBreadcrumbsParams, } from '@kbn/core-chrome-browser'; import type { CustomBrandingStart } from '@kbn/core-custom-branding-browser'; -import type { SideNavComponent as ISideNavComponent } from '@kbn/core-chrome-browser'; -import { KIBANA_ASK_ELASTIC_LINK } from './constants'; +import type { + SideNavComponent as ISideNavComponent, + ChromeHelpMenuLink, +} from '@kbn/core-chrome-browser'; + import { DocTitleService } from './doc_title'; import { NavControlsService } from './nav_controls'; import { NavLinksService } from './nav_links'; @@ -135,7 +138,7 @@ export class ChromeService { >(undefined); const badge$ = new BehaviorSubject(undefined); const customNavLink$ = new BehaviorSubject(undefined); - const helpSupportUrl$ = new BehaviorSubject(KIBANA_ASK_ELASTIC_LINK); + const helpSupportUrl$ = new BehaviorSubject(docLinks.links.kibana.askElastic); const isNavDrawerLocked$ = new BehaviorSubject(localStorage.getItem(IS_LOCKED_KEY) === 'true'); const chromeStyle$ = new BehaviorSubject('classic'); @@ -168,6 +171,7 @@ export class ChromeService { const recentlyAccessed = await this.recentlyAccessed.start({ http }); const docTitle = this.docTitle.start(); const { customBranding$ } = customBranding; + const helpMenuLinks$ = navControls.getHelpMenuLinks$(); // erase chrome fields from a previous app while switching to a next app application.currentAppId$.subscribe(() => { @@ -301,12 +305,13 @@ export class ChromeService { breadcrumbs$={currentProjectBreadcrumbs$} helpExtension$={helpExtension$.pipe(takeUntil(this.stop$))} helpSupportUrl$={helpSupportUrl$.pipe(takeUntil(this.stop$))} + helpMenuLinks$={helpMenuLinks$} navControlsLeft$={navControls.getLeft$()} navControlsCenter$={navControls.getCenter$()} navControlsRight$={navControls.getRight$()} loadingCount$={http.getLoadingCount$()} homeHref$={projectNavigation.getProjectHome$()} - kibanaDocLink={docLinks.links.kibana.guide} + docLinks={docLinks} kibanaVersion={injectedMetadata.getKibanaVersion()} prependBasePath={http.basePath.prepend} > @@ -329,10 +334,12 @@ export class ChromeService { breadcrumbsAppendExtension$={breadcrumbsAppendExtension$.pipe(takeUntil(this.stop$))} customNavLink$={customNavLink$.pipe(takeUntil(this.stop$))} kibanaDocLink={docLinks.links.kibana.guide} + docLinks={docLinks} forceAppSwitcherNavigation$={navLinks.getForceAppSwitcherNavigation$()} globalHelpExtensionMenuLinks$={globalHelpExtensionMenuLinks$} helpExtension$={helpExtension$.pipe(takeUntil(this.stop$))} helpSupportUrl$={helpSupportUrl$.pipe(takeUntil(this.stop$))} + helpMenuLinks$={helpMenuLinks$} homeHref={http.basePath.prepend('/app/home')} isVisible$={this.isVisible$} kibanaVersion={injectedMetadata.getKibanaVersion()} @@ -399,6 +406,8 @@ export class ChromeService { setHelpSupportUrl: (url: string) => helpSupportUrl$.next(url), + getHelpSupportUrl$: () => helpSupportUrl$.pipe(takeUntil(this.stop$)), + getIsNavDrawerLocked$: () => getIsNavDrawerLocked$, getCustomNavLink$: () => customNavLink$.pipe(takeUntil(this.stop$)), @@ -407,6 +416,10 @@ export class ChromeService { customNavLink$.next(customNavLink); }, + setHelpMenuLinks: (helpMenuLinks: ChromeHelpMenuLink[]) => { + navControls.setHelpMenuLinks(helpMenuLinks); + }, + setHeaderBanner: (headerBanner?: ChromeUserBanner) => { headerBanner$.next(headerBanner); }, diff --git a/packages/core/chrome/core-chrome-browser-internal/src/nav_controls/nav_controls_service.ts b/packages/core/chrome/core-chrome-browser-internal/src/nav_controls/nav_controls_service.ts index 36f448b187e13..1dada1d132459 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/nav_controls/nav_controls_service.ts +++ b/packages/core/chrome/core-chrome-browser-internal/src/nav_controls/nav_controls_service.ts @@ -9,7 +9,11 @@ import { sortBy } from 'lodash'; import { BehaviorSubject, ReplaySubject } from 'rxjs'; import { map, takeUntil } from 'rxjs/operators'; -import type { ChromeNavControl, ChromeNavControls } from '@kbn/core-chrome-browser'; +import type { + ChromeNavControl, + ChromeNavControls, + ChromeHelpMenuLink, +} from '@kbn/core-chrome-browser'; /** @internal */ export class NavControlsService { @@ -20,6 +24,7 @@ export class NavControlsService { const navControlsRight$ = new BehaviorSubject>(new Set()); const navControlsCenter$ = new BehaviorSubject>(new Set()); const navControlsExtension$ = new BehaviorSubject>(new Set()); + const helpMenuLinks$ = new BehaviorSubject([]); return { // In the future, registration should be moved to the setup phase. This @@ -36,6 +41,14 @@ export class NavControlsService { registerExtension: (navControl: ChromeNavControl) => navControlsExtension$.next(new Set([...navControlsExtension$.value.values(), navControl])), + setHelpMenuLinks: (links: ChromeHelpMenuLink[]) => { + // This extension point is only intended to be used once by the cloud integration > cloud_links plugin + if (helpMenuLinks$.value.length > 0) { + throw new Error(`Help menu links have already been set`); + } + helpMenuLinks$.next(links); + }, + getLeft$: () => navControlsLeft$.pipe( map((controls) => sortBy([...controls.values()], 'order')), @@ -56,6 +69,7 @@ export class NavControlsService { map((controls) => sortBy([...controls.values()], 'order')), takeUntil(this.stop$) ), + getHelpMenuLinks$: () => helpMenuLinks$.pipe(takeUntil(this.stop$)), }; } diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header.test.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header.test.tsx index 1e05dafde05cd..fa57857904617 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header.test.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header.test.tsx @@ -8,10 +8,11 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; -import { BehaviorSubject } from 'rxjs'; +import { BehaviorSubject, of } from 'rxjs'; import { StubBrowserStorage, mountWithIntl } from '@kbn/test-jest-helpers'; import { httpServiceMock } from '@kbn/core-http-browser-mocks'; import { applicationServiceMock } from '@kbn/core-application-browser-mocks'; +import { docLinksServiceMock } from '@kbn/core-doc-links-browser-mocks'; import type { ChromeBreadcrumbsAppendExtension } from '@kbn/core-chrome-browser'; import { Header } from './header'; @@ -30,6 +31,7 @@ function mockProps() { isVisible$: new BehaviorSubject(true), customBranding$: new BehaviorSubject({}), kibanaDocLink: '/docs', + docLinks: docLinksServiceMock.createStartContract(), navLinks$: new BehaviorSubject([]), customNavLink$: new BehaviorSubject(undefined), recentlyAccessed$: new BehaviorSubject([]), @@ -87,6 +89,7 @@ describe('Header', () => { customNavLink$={customNavLink$} breadcrumbsAppendExtension$={breadcrumbsAppendExtension$} headerBanner$={headerBanner$} + helpMenuLinks$={of([])} /> ); expect(component.find('EuiHeader').exists()).toBeFalsy(); diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header.tsx index f1867cbea256d..1a23c0a82f55b 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header.tsx @@ -27,6 +27,7 @@ import type { ChromeBreadcrumb, ChromeNavControl, ChromeNavLink, + ChromeHelpMenuLink, ChromeRecentlyAccessedHistoryItem, ChromeBreadcrumbsAppendExtension, ChromeHelpExtension, @@ -34,6 +35,7 @@ import type { ChromeUserBanner, } from '@kbn/core-chrome-browser'; import { CustomBranding } from '@kbn/core-custom-branding-common'; +import type { DocLinksStart } from '@kbn/core-doc-links-browser'; import { LoadingIndicator } from '../loading_indicator'; import type { OnIsLockedUpdate } from './types'; import { CollapsibleNav } from './collapsible_nav'; @@ -59,12 +61,14 @@ export interface HeaderProps { homeHref: string; isVisible$: Observable; kibanaDocLink: string; + docLinks: DocLinksStart; navLinks$: Observable; recentlyAccessed$: Observable; forceAppSwitcherNavigation$: Observable; globalHelpExtensionMenuLinks$: Observable; helpExtension$: Observable; helpSupportUrl$: Observable; + helpMenuLinks$: Observable; navControlsLeft$: Observable; navControlsCenter$: Observable; navControlsRight$: Observable; @@ -79,6 +83,7 @@ export interface HeaderProps { export function Header({ kibanaVersion, kibanaDocLink, + docLinks, application, basePath, onIsLockedUpdate, @@ -163,7 +168,9 @@ export function Header({ globalHelpExtensionMenuLinks$={globalHelpExtensionMenuLinks$} helpExtension$={observables.helpExtension$} helpSupportUrl$={observables.helpSupportUrl$} + defaultContentLinks$={observables.helpMenuLinks$} kibanaDocLink={kibanaDocLink} + docLinks={docLinks} kibanaVersion={kibanaVersion} navigateToUrl={application.navigateToUrl} />, diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_help_menu.test.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_help_menu.test.tsx index d66982516d251..5518667445a57 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_help_menu.test.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_help_menu.test.tsx @@ -10,6 +10,8 @@ import React from 'react'; import { BehaviorSubject, of } from 'rxjs'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { applicationServiceMock } from '@kbn/core-application-browser-mocks'; +import { docLinksServiceMock } from '@kbn/core-doc-links-browser-mocks'; + import { HeaderHelpMenu } from './header_help_menu'; describe('HeaderHelpMenu', () => { @@ -26,14 +28,23 @@ describe('HeaderHelpMenu', () => { helpSupportUrl$={helpSupportUrl$} kibanaVersion={'version'} kibanaDocLink={''} + docLinks={docLinksServiceMock.createStartContract()} + defaultContentLinks$={of([])} /> ); expect(component.find('EuiButtonEmpty').length).toBe(1); // only the toggle view on/off button component.find('EuiButtonEmpty').simulate('click'); - // 4 default links + the toggle button - expect(component.find('EuiButtonEmpty').length).toBe(5); + const buttons = component.find('EuiButtonEmpty'); + const buttonTexts = buttons.map((button) => button.text()).filter((text) => text.trim() !== ''); + + expect(buttonTexts).toEqual([ + 'Kibana documentation', + 'Ask Elastic', + 'Give feedback', + 'Open an issue in GitHub', + ]); }); test('it renders the global custom content + the default content', () => { @@ -63,6 +74,8 @@ describe('HeaderHelpMenu', () => { helpSupportUrl$={helpSupportUrl$} kibanaVersion={'version'} kibanaDocLink={''} + docLinks={docLinksServiceMock.createStartContract()} + defaultContentLinks$={of([])} /> ); diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_help_menu.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_help_menu.tsx index 1d4813763a201..e1e43d43ab401 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_help_menu.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/header_help_menu.tsx @@ -29,17 +29,56 @@ import type { ChromeHelpExtension, ChromeGlobalHelpExtensionMenuLink, } from '@kbn/core-chrome-browser'; -import { GITHUB_CREATE_ISSUE_LINK, KIBANA_FEEDBACK_LINK } from '../../constants'; +import type { ChromeHelpMenuLink } from '@kbn/core-chrome-browser/src'; +import type { DocLinksStart } from '@kbn/core-doc-links-browser'; + import { HeaderExtension } from './header_extension'; import { isModifiedOrPrevented } from './nav_link'; +const buildDefaultContentLinks = ({ + kibanaDocLink, + docLinks, + helpSupportUrl, +}: { + kibanaDocLink: string; + docLinks: DocLinksStart; + helpSupportUrl: string; +}): ChromeHelpMenuLink[] => [ + { + title: i18n.translate('core.ui.chrome.headerGlobalNav.helpMenuKibanaDocumentationTitle', { + defaultMessage: 'Kibana documentation', + }), + href: kibanaDocLink, + }, + { + title: i18n.translate('core.ui.chrome.headerGlobalNav.helpMenuAskElasticTitle', { + defaultMessage: 'Ask Elastic', + }), + href: helpSupportUrl, + }, + { + title: i18n.translate('core.ui.chrome.headerGlobalNav.helpMenuGiveFeedbackTitle', { + defaultMessage: 'Give feedback', + }), + href: docLinks.links.kibana.feedback, + }, + { + title: i18n.translate('core.ui.chrome.headerGlobalNav.helpMenuOpenGitHubIssueTitle', { + defaultMessage: 'Open an issue in GitHub', + }), + href: docLinks.links.kibana.createGithubIssue, + }, +]; + interface Props { navigateToUrl: InternalApplicationStart['navigateToUrl']; globalHelpExtensionMenuLinks$: Observable; helpExtension$: Observable; helpSupportUrl$: Observable; + defaultContentLinks$: Observable; kibanaVersion: string; kibanaDocLink: string; + docLinks: DocLinksStart; } interface State { @@ -47,6 +86,7 @@ interface State { helpExtension?: ChromeHelpExtension; helpSupportUrl: string; globalHelpExtensionMenuLinks: ChromeGlobalHelpExtensionMenuLink[]; + defaultContentLinks: ChromeHelpMenuLink[]; } export class HeaderHelpMenu extends Component { @@ -60,6 +100,7 @@ export class HeaderHelpMenu extends Component { helpExtension: undefined, helpSupportUrl: '', globalHelpExtensionMenuLinks: [], + defaultContentLinks: [], }; } @@ -67,14 +108,21 @@ export class HeaderHelpMenu extends Component { this.subscription = combineLatest( this.props.helpExtension$, this.props.helpSupportUrl$, - this.props.globalHelpExtensionMenuLinks$ - ).subscribe(([helpExtension, helpSupportUrl, globalHelpExtensionMenuLinks]) => { - this.setState({ - helpExtension, - helpSupportUrl, - globalHelpExtensionMenuLinks, - }); - }); + this.props.globalHelpExtensionMenuLinks$, + this.props.defaultContentLinks$ + ).subscribe( + ([helpExtension, helpSupportUrl, globalHelpExtensionMenuLinks, defaultContentLinks]) => { + this.setState({ + helpExtension, + helpSupportUrl, + globalHelpExtensionMenuLinks, + defaultContentLinks: + defaultContentLinks.length === 0 + ? buildDefaultContentLinks({ ...this.props, helpSupportUrl }) + : defaultContentLinks, + }); + } + ); } public componentWillUnmount() { @@ -137,58 +185,33 @@ export class HeaderHelpMenu extends Component {
{globalCustomContent} {defaultContent} - {(defaultContent || customContent) && } - {customContent} + {customContent && ( + <> + + {customContent} + + )}
); } private renderDefaultContent() { - const { kibanaDocLink } = this.props; - const { helpSupportUrl } = this.state; + const { defaultContentLinks } = this.state; return ( - - - - - - - - - - - - - - - - - - - - - + {defaultContentLinks.map(({ href, title, iconType }, i) => { + const isLast = i === defaultContentLinks.length - 1; + return ( + + + {title} + + {!isLast && } + + ); + })} ); } diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.test.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.test.tsx index 08ea2bcd5ba58..419e087436de3 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.test.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.test.tsx @@ -8,6 +8,7 @@ import { EuiHeader } from '@elastic/eui'; import { applicationServiceMock } from '@kbn/core-application-browser-mocks'; +import { docLinksServiceMock } from '@kbn/core-doc-links-browser-mocks'; import { fireEvent, render, screen } from '@testing-library/react'; import React from 'react'; import * as Rx from 'rxjs'; @@ -20,10 +21,11 @@ describe('Header', () => { application: mockApplication, breadcrumbs$: Rx.of([]), actionMenu$: Rx.of(undefined), - kibanaDocLink: 'app/help/doclinks', + docLinks: docLinksServiceMock.createStartContract(), globalHelpExtensionMenuLinks$: Rx.of([]), helpExtension$: Rx.of(undefined), helpSupportUrl$: Rx.of('app/help'), + helpMenuLinks$: Rx.of([]), homeHref$: Rx.of('app/home'), kibanaVersion: '8.9', loadingCount$: Rx.of(0), diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx index 92e40748c3ac2..1da457d289db1 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/project/header.tsx @@ -24,6 +24,7 @@ import { ChromeBreadcrumb, ChromeGlobalHelpExtensionMenuLink, ChromeHelpExtension, + ChromeHelpMenuLink, ChromeNavControl, } from '@kbn/core-chrome-browser/src'; import type { HttpStart } from '@kbn/core-http-browser'; @@ -34,6 +35,8 @@ import { Router } from '@kbn/shared-ux-router'; import useLocalStorage from 'react-use/lib/useLocalStorage'; import useObservable from 'react-use/lib/useObservable'; import { Observable, debounceTime } from 'rxjs'; +import type { DocLinksStart } from '@kbn/core-doc-links-browser'; + import { HeaderActionMenu, useHeaderActionMenuMounter } from '../header/header_action_menu'; import { HeaderBreadcrumbs } from '../header/header_breadcrumbs'; import { HeaderHelpMenu } from '../header/header_help_menu'; @@ -87,11 +90,12 @@ const headerStrings = { export interface Props { breadcrumbs$: Observable; actionMenu$: Observable; - kibanaDocLink: string; + docLinks: DocLinksStart; children: React.ReactNode; globalHelpExtensionMenuLinks$: Observable; helpExtension$: Observable; helpSupportUrl$: Observable; + helpMenuLinks$: Observable; homeHref$: Observable; kibanaVersion: string; application: InternalApplicationStart; @@ -158,10 +162,10 @@ const Logo = ( export const ProjectHeader = ({ application, - kibanaDocLink, kibanaVersion, children, prependBasePath, + docLinks, ...observables }: Props) => { const [navId] = useState(htmlIdGenerator()()); @@ -239,7 +243,9 @@ export const ProjectHeader = ({ globalHelpExtensionMenuLinks$={observables.globalHelpExtensionMenuLinks$} helpExtension$={observables.helpExtension$} helpSupportUrl$={observables.helpSupportUrl$} - kibanaDocLink={kibanaDocLink} + defaultContentLinks$={observables.helpMenuLinks$} + kibanaDocLink={docLinks.links.elasticStackGetStarted} + docLinks={docLinks} kibanaVersion={kibanaVersion} navigateToUrl={application.navigateToUrl} /> diff --git a/packages/core/chrome/core-chrome-browser-mocks/src/chrome_service.mock.ts b/packages/core/chrome/core-chrome-browser-mocks/src/chrome_service.mock.ts index 76192aff162c7..f1ce84f2f9be1 100644 --- a/packages/core/chrome/core-chrome-browser-mocks/src/chrome_service.mock.ts +++ b/packages/core/chrome/core-chrome-browser-mocks/src/chrome_service.mock.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { BehaviorSubject } from 'rxjs'; +import { BehaviorSubject, of } from 'rxjs'; import type { PublicMethodsOf } from '@kbn/utility-types'; import type { DeeplyMockedKeys } from '@kbn/utility-types-jest'; import type { ChromeBadge, ChromeBreadcrumb } from '@kbn/core-chrome-browser'; @@ -41,6 +41,8 @@ const createStartContractMock = () => { getCenter$: jest.fn(), getRight$: jest.fn(), getExtension$: jest.fn(), + setHelpMenuLinks: jest.fn(), + getHelpMenuLinks$: jest.fn(), }, setIsVisible: jest.fn(), getIsVisible$: jest.fn(), @@ -54,7 +56,9 @@ const createStartContractMock = () => { registerGlobalHelpExtensionMenuLink: jest.fn(), getHelpExtension$: jest.fn(), setHelpExtension: jest.fn(), + setHelpMenuLinks: jest.fn(), setHelpSupportUrl: jest.fn(), + getHelpSupportUrl$: jest.fn(() => of('https://www.elastic.co/support')), getIsNavDrawerLocked$: jest.fn(), getCustomNavLink$: jest.fn(), setCustomNavLink: jest.fn(), diff --git a/packages/core/chrome/core-chrome-browser/index.ts b/packages/core/chrome/core-chrome-browser/index.ts index c2d7243f34d6f..d42d859cc6f3b 100644 --- a/packages/core/chrome/core-chrome-browser/index.ts +++ b/packages/core/chrome/core-chrome-browser/index.ts @@ -14,6 +14,7 @@ export type { ChromeBreadcrumbsAppendExtension, ChromeDocTitle, ChromeGlobalHelpExtensionMenuLink, + ChromeHelpMenuLink, ChromeHelpExtension, ChromeHelpExtensionLinkBase, ChromeHelpExtensionMenuCustomLink, diff --git a/packages/core/chrome/core-chrome-browser/src/contracts.ts b/packages/core/chrome/core-chrome-browser/src/contracts.ts index f64995c877c7f..c9893ed3863ce 100644 --- a/packages/core/chrome/core-chrome-browser/src/contracts.ts +++ b/packages/core/chrome/core-chrome-browser/src/contracts.ts @@ -10,7 +10,7 @@ import type { Observable } from 'rxjs'; import type { ChromeNavLink, ChromeNavLinks } from './nav_links'; import type { ChromeRecentlyAccessed } from './recently_accessed'; import type { ChromeDocTitle } from './doc_title'; -import type { ChromeNavControls } from './nav_controls'; +import type { ChromeHelpMenuLink, ChromeNavControls } from './nav_controls'; import type { ChromeHelpExtension } from './help_extension'; import type { ChromeBreadcrumb, ChromeBreadcrumbsAppendExtension } from './breadcrumb'; import type { ChromeBadge, ChromeStyle, ChromeUserBanner } from './types'; @@ -106,6 +106,11 @@ export interface ChromeStart { */ setCustomNavLink(newCustomNavLink?: Partial): void; + /** + * Override the default links shown in the help menu + */ + setHelpMenuLinks(links: ChromeHelpMenuLink[]): void; + /** * Get the list of the registered global help extension menu links */ @@ -134,6 +139,11 @@ export interface ChromeStart { */ setHelpSupportUrl(url: string): void; + /** + * Get the support URL shown in the help menu + */ + getHelpSupportUrl$(): Observable; + /** * Get an observable of the current locked state of the nav drawer. */ diff --git a/packages/core/chrome/core-chrome-browser/src/index.ts b/packages/core/chrome/core-chrome-browser/src/index.ts index 858b1c4e3647a..7a414fc87164e 100644 --- a/packages/core/chrome/core-chrome-browser/src/index.ts +++ b/packages/core/chrome/core-chrome-browser/src/index.ts @@ -20,7 +20,7 @@ export type { ChromeHelpExtensionMenuGitHubLink, ChromeGlobalHelpExtensionMenuLink, } from './help_extension'; -export type { ChromeNavControls, ChromeNavControl } from './nav_controls'; +export type { ChromeNavControls, ChromeNavControl, ChromeHelpMenuLink } from './nav_controls'; export type { ChromeNavLinks, ChromeNavLink } from './nav_links'; export type { ChromeRecentlyAccessed, diff --git a/packages/core/chrome/core-chrome-browser/src/nav_controls.ts b/packages/core/chrome/core-chrome-browser/src/nav_controls.ts index 44529b1edf122..39b5d1b3b59b1 100644 --- a/packages/core/chrome/core-chrome-browser/src/nav_controls.ts +++ b/packages/core/chrome/core-chrome-browser/src/nav_controls.ts @@ -15,6 +15,13 @@ export interface ChromeNavControl { mount: MountPoint; } +/** @public */ +export interface ChromeHelpMenuLink { + title: string; + href: string; + iconType?: string; +} + /** * {@link ChromeNavControls | APIs} for registering new controls to be displayed in the navigation bar. * @@ -44,6 +51,9 @@ export interface ChromeNavControls { /** Register an extension to be presented to the left of the top-right side of the chrome header. */ registerExtension(navControl: ChromeNavControl): void; + /** Set the help menu links */ + setHelpMenuLinks(links: ChromeHelpMenuLink[]): void; + /** @internal */ getLeft$(): Observable; @@ -55,4 +65,7 @@ export interface ChromeNavControls { /** @internal */ getExtension$(): Observable; + + /** @internal */ + getHelpMenuLinks$(): Observable; } diff --git a/packages/core/http/core-http-router-server-internal/src/router.ts b/packages/core/http/core-http-router-server-internal/src/router.ts index 7aede8bfc01a7..509f407ff401a 100644 --- a/packages/core/http/core-http-router-server-internal/src/router.ts +++ b/packages/core/http/core-http-router-server-internal/src/router.ts @@ -7,6 +7,7 @@ */ import type { Request, ResponseToolkit } from '@hapi/hapi'; +import apm from 'elastic-apm-node'; import { isConfigSchema } from '@kbn/config-schema'; import type { Logger } from '@kbn/logging'; import { @@ -206,18 +207,24 @@ export class Router = undefined; + public get versioned(): VersionedRouter { if (this.versionedRouter === undefined) { this.versionedRouter = CoreVersionedRouter.from({ diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/utils/included_fields.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/utils/included_fields.ts index e0a1f2ca9b02b..50686def81c88 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/utils/included_fields.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/utils/included_fields.ts @@ -11,7 +11,7 @@ const ROOT_FIELDS = [ 'namespaces', 'type', 'references', - 'migrationVersion', + 'migrationVersion', // deprecated, see https://github.com/elastic/kibana/pull/150075 'coreMigrationVersion', 'typeMigrationVersion', 'managed', diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/check_target_mappings.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/check_target_mappings.test.ts index c98dec8ee48a8..17c55102a3cc7 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/check_target_mappings.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/check_target_mappings.test.ts @@ -8,22 +8,35 @@ import * as Either from 'fp-ts/lib/Either'; import type { IndexMapping } from '@kbn/core-saved-objects-base-server-internal'; -import { checkTargetMappings } from './check_target_mappings'; -import { diffMappings } from '../core/build_active_mappings'; +import type { SavedObjectsMappingProperties } from '@kbn/core-saved-objects-server'; +import { + checkTargetMappings, + type ComparedMappingsChanged, + type ComparedMappingsMatch, +} from './check_target_mappings'; +import { getUpdatedHashes } from '../core/build_active_mappings'; jest.mock('../core/build_active_mappings'); -const diffMappingsMock = diffMappings as jest.MockedFn; +const getUpdatedHashesMock = getUpdatedHashes as jest.MockedFn; -const actualMappings: IndexMapping = { - properties: { - field: { type: 'integer' }, - }, +const indexTypes = ['type1', 'type2']; + +const properties: SavedObjectsMappingProperties = { + type1: { type: 'long' }, + type2: { type: 'long' }, +}; + +const migrationMappingPropertyHashes = { + type1: 'type1Hash', + type2: 'type2Hash', }; const expectedMappings: IndexMapping = { - properties: { - field: { type: 'long' }, + properties, + dynamic: 'strict', + _meta: { + migrationMappingPropertyHashes, }, }; @@ -32,48 +45,99 @@ describe('checkTargetMappings', () => { jest.clearAllMocks(); }); - it('returns match=false if source mappings are not defined', async () => { - const task = checkTargetMappings({ - expectedMappings, - }); - - const result = await task(); - expect(diffMappings).not.toHaveBeenCalled(); - expect(result).toEqual(Either.right({ match: false })); - }); + describe('when actual mappings are incomplete', () => { + it("returns 'actual_mappings_incomplete' if actual mappings are not defined", async () => { + const task = checkTargetMappings({ + indexTypes, + expectedMappings, + }); - it('calls diffMappings() with the source and target mappings', async () => { - const task = checkTargetMappings({ - actualMappings, - expectedMappings, + const result = await task(); + expect(result).toEqual(Either.left({ type: 'actual_mappings_incomplete' as const })); }); - await task(); - expect(diffMappings).toHaveBeenCalledTimes(1); - expect(diffMappings).toHaveBeenCalledWith(actualMappings, expectedMappings); - }); - - it('returns match=true if diffMappings() match', async () => { - diffMappingsMock.mockReturnValueOnce(undefined); + it("returns 'actual_mappings_incomplete' if actual mappings do not define _meta", async () => { + const task = checkTargetMappings({ + indexTypes, + expectedMappings, + actualMappings: { + properties, + dynamic: 'strict', + }, + }); + + const result = await task(); + expect(result).toEqual(Either.left({ type: 'actual_mappings_incomplete' as const })); + }); - const task = checkTargetMappings({ - actualMappings, - expectedMappings, + it("returns 'actual_mappings_incomplete' if actual mappings do not define migrationMappingPropertyHashes", async () => { + const task = checkTargetMappings({ + indexTypes, + expectedMappings, + actualMappings: { + properties, + dynamic: 'strict', + _meta: {}, + }, + }); + + const result = await task(); + expect(result).toEqual(Either.left({ type: 'actual_mappings_incomplete' as const })); }); - const result = await task(); - expect(result).toEqual(Either.right({ match: true })); + it("returns 'actual_mappings_incomplete' if actual mappings define a different value for 'dynamic' property", async () => { + const task = checkTargetMappings({ + indexTypes, + expectedMappings, + actualMappings: { + properties, + dynamic: false, + _meta: { migrationMappingPropertyHashes }, + }, + }); + + const result = await task(); + expect(result).toEqual(Either.left({ type: 'actual_mappings_incomplete' as const })); + }); }); - it('returns match=false if diffMappings() finds differences', async () => { - diffMappingsMock.mockReturnValueOnce({ changedProp: 'field' }); - - const task = checkTargetMappings({ - actualMappings, - expectedMappings, + describe('when actual mappings are complete', () => { + describe('and mappings do not match', () => { + it('returns the lists of changed root fields and types', async () => { + const task = checkTargetMappings({ + indexTypes, + expectedMappings, + actualMappings: expectedMappings, + }); + + getUpdatedHashesMock.mockReturnValueOnce(['type1', 'type2', 'someRootField']); + + const result = await task(); + const expected: ComparedMappingsChanged = { + type: 'compared_mappings_changed' as const, + updatedRootFields: ['someRootField'], + updatedTypes: ['type1', 'type2'], + }; + expect(result).toEqual(Either.left(expected)); + }); }); - const result = await task(); - expect(result).toEqual(Either.right({ match: false })); + describe('and mappings match', () => { + it('returns a compared_mappings_match response', async () => { + const task = checkTargetMappings({ + indexTypes, + expectedMappings, + actualMappings: expectedMappings, + }); + + getUpdatedHashesMock.mockReturnValueOnce([]); + + const result = await task(); + const expected: ComparedMappingsMatch = { + type: 'compared_mappings_match' as const, + }; + expect(result).toEqual(Either.right(expected)); + }); + }); }); }); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/check_target_mappings.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/check_target_mappings.ts index 876ce4873d447..26e0f074f43cb 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/check_target_mappings.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/check_target_mappings.ts @@ -8,29 +8,62 @@ import * as Either from 'fp-ts/lib/Either'; import * as TaskEither from 'fp-ts/lib/TaskEither'; -import { IndexMapping } from '@kbn/core-saved-objects-base-server-internal'; -import { diffMappings } from '../core/build_active_mappings'; +import type { IndexMapping } from '@kbn/core-saved-objects-base-server-internal'; +import { getUpdatedHashes } from '../core/build_active_mappings'; /** @internal */ export interface CheckTargetMappingsParams { + indexTypes: string[]; actualMappings?: IndexMapping; expectedMappings: IndexMapping; } /** @internal */ -export interface TargetMappingsCompareResult { - match: boolean; +export interface ComparedMappingsMatch { + type: 'compared_mappings_match'; +} + +export interface ActualMappingsIncomplete { + type: 'actual_mappings_incomplete'; +} + +export interface ComparedMappingsChanged { + type: 'compared_mappings_changed'; + updatedRootFields: string[]; + updatedTypes: string[]; } export const checkTargetMappings = ({ + indexTypes, actualMappings, expectedMappings, - }: CheckTargetMappingsParams): TaskEither.TaskEither => + }: CheckTargetMappingsParams): TaskEither.TaskEither< + ActualMappingsIncomplete | ComparedMappingsChanged, + ComparedMappingsMatch + > => async () => { - if (!actualMappings) { - return Either.right({ match: false }); + if ( + !actualMappings?._meta?.migrationMappingPropertyHashes || + actualMappings.dynamic !== expectedMappings.dynamic + ) { + return Either.left({ type: 'actual_mappings_incomplete' as const }); + } + + const updatedHashes = getUpdatedHashes({ + actual: actualMappings, + expected: expectedMappings, + }); + + if (updatedHashes.length) { + const updatedTypes = updatedHashes.filter((field) => indexTypes.includes(field)); + const updatedRootFields = updatedHashes.filter((field) => !indexTypes.includes(field)); + return Either.left({ + type: 'compared_mappings_changed' as const, + updatedRootFields, + updatedTypes, + }); + } else { + return Either.right({ type: 'compared_mappings_match' as const }); } - const diff = diffMappings(actualMappings, expectedMappings); - return Either.right({ match: !diff }); }; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/index.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/index.ts index 5af2471a7f72e..270926a10cbab 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/index.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/index.ts @@ -108,6 +108,7 @@ import type { UnknownDocsFound } from './check_for_unknown_docs'; import type { IncompatibleClusterRoutingAllocation } from './initialize_action'; import type { ClusterShardLimitExceeded } from './create_index'; import type { SynchronizationFailed } from './synchronize_migrators'; +import type { ActualMappingsIncomplete, ComparedMappingsChanged } from './check_target_mappings'; export type { CheckForUnknownDocsParams, @@ -176,6 +177,8 @@ export interface ActionErrorTypeMap { cluster_shard_limit_exceeded: ClusterShardLimitExceeded; es_response_too_large: EsResponseTooLargeError; synchronization_failed: SynchronizationFailed; + actual_mappings_incomplete: ActualMappingsIncomplete; + compared_mappings_changed: ComparedMappingsChanged; } /** diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/pickup_updated_mappings.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/pickup_updated_mappings.ts index 8b6205cb7cc6f..632127dd2e084 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/pickup_updated_mappings.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/pickup_updated_mappings.ts @@ -9,6 +9,7 @@ import * as Either from 'fp-ts/lib/Either'; import * as TaskEither from 'fp-ts/lib/TaskEither'; import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; +import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import { catchRetryableEsClientErrors, type RetryableEsClientError, @@ -20,7 +21,7 @@ export interface UpdateByQueryResponse { /** * Pickup updated mappings by performing an update by query operation on all - * documents in the index. Returns a task ID which can be + * documents matching the passed in query. Returns a task ID which can be * tracked for progress. * * @remarks When mappings are updated to add a field which previously wasn't @@ -35,7 +36,8 @@ export const pickupUpdatedMappings = ( client: ElasticsearchClient, index: string, - batchSize: number + batchSize: number, + query?: QueryDslQueryContainer ): TaskEither.TaskEither => () => { return client @@ -52,6 +54,8 @@ export const pickupUpdatedMappings = refresh: true, // Create a task and return task id instead of blocking until complete wait_for_completion: false, + // Only update the documents that match the provided query + query, }) .then(({ task: taskId }) => { return Either.right({ taskId: String(taskId!) }); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/update_and_pickup_mappings.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/update_and_pickup_mappings.test.ts index 6b227ea2ef66a..2aa4cdbd2830d 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/update_and_pickup_mappings.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/update_and_pickup_mappings.test.ts @@ -11,47 +11,76 @@ import { errors as EsErrors } from '@elastic/elasticsearch'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; import { updateAndPickupMappings } from './update_and_pickup_mappings'; import { DEFAULT_TIMEOUT } from './constants'; +import { pickupUpdatedMappings } from './pickup_updated_mappings'; jest.mock('./catch_retryable_es_client_errors'); +jest.mock('./pickup_updated_mappings'); describe('updateAndPickupMappings', () => { beforeEach(() => { jest.clearAllMocks(); }); - // Create a mock client that rejects all methods with a 503 status code - // response. - const retryableError = new EsErrors.ResponseError( - elasticsearchClientMock.createApiResponse({ - statusCode: 503, - body: { error: { type: 'es_type', reason: 'es_reason' } }, - }) - ); - const client = elasticsearchClientMock.createInternalClient( - elasticsearchClientMock.createErrorTransportRequestPromise(retryableError) - ); + describe('putMappingTask', () => { + // Create a mock client that rejects all methods with a 503 status code + // response. + const retryableError = new EsErrors.ResponseError( + elasticsearchClientMock.createApiResponse({ + statusCode: 503, + body: { error: { type: 'es_type', reason: 'es_reason' } }, + }) + ); + const client = elasticsearchClientMock.createInternalClient( + elasticsearchClientMock.createErrorTransportRequestPromise(retryableError) + ); - it('calls catchRetryableEsClientErrors when the promise rejects', async () => { - const task = updateAndPickupMappings({ - client, - index: 'new_index', - mappings: { properties: {} }, - batchSize: 1000, + it('calls catchRetryableEsClientErrors when the promise rejects', async () => { + const task = updateAndPickupMappings({ + client, + index: 'new_index', + mappings: { properties: {} }, + batchSize: 1000, + }); + try { + await task(); + } catch (e) { + /** ignore */ + } + + expect(catchRetryableEsClientErrors).toHaveBeenCalledWith(retryableError); }); - try { - await task(); - } catch (e) { - /** ignore */ - } - expect(catchRetryableEsClientErrors).toHaveBeenCalledWith(retryableError); - }); + it('calls the indices.putMapping with the mapping properties as well as the _meta information', async () => { + const task = updateAndPickupMappings({ + client, + index: 'new_index', + mappings: { + properties: { + 'apm-indices': { + type: 'object', + dynamic: false, + }, + }, + _meta: { + migrationMappingPropertyHashes: { + references: '7997cf5a56cc02bdc9c93361bde732b0', + 'epm-packages': '860e23f4404fa1c33f430e6dad5d8fa2', + 'cases-connector-mappings': '17d2e9e0e170a21a471285a5d845353c', + }, + }, + }, + batchSize: 1000, + }); + try { + await task(); + } catch (e) { + /** ignore */ + } - it('calls the indices.putMapping with the mapping properties as well as the _meta information', async () => { - const task = updateAndPickupMappings({ - client, - index: 'new_index', - mappings: { + expect(client.indices.putMapping).toHaveBeenCalledTimes(1); + expect(client.indices.putMapping).toHaveBeenCalledWith({ + index: 'new_index', + timeout: DEFAULT_TIMEOUT, properties: { 'apm-indices': { type: 'object', @@ -65,32 +94,47 @@ describe('updateAndPickupMappings', () => { 'cases-connector-mappings': '17d2e9e0e170a21a471285a5d845353c', }, }, - }, - batchSize: 1000, + }); }); - try { - await task(); - } catch (e) { - /** ignore */ - } + }); - expect(client.indices.putMapping).toHaveBeenCalledTimes(1); - expect(client.indices.putMapping).toHaveBeenCalledWith({ - index: 'new_index', - timeout: DEFAULT_TIMEOUT, - properties: { - 'apm-indices': { - type: 'object', - dynamic: false, - }, - }, - _meta: { - migrationMappingPropertyHashes: { - references: '7997cf5a56cc02bdc9c93361bde732b0', - 'epm-packages': '860e23f4404fa1c33f430e6dad5d8fa2', - 'cases-connector-mappings': '17d2e9e0e170a21a471285a5d845353c', + describe('pickupUpdatedMappings', () => { + const client = elasticsearchClientMock.createInternalClient( + elasticsearchClientMock.createSuccessTransportRequestPromise({}) + ); + + it('calls pickupUpdatedMappings with the right parameters', async () => { + const query = { + bool: { + should: [ + { + term: { + type: 'type1', + }, + }, + { + term: { + type: 'type2', + }, + }, + ], }, - }, + }; + const task = updateAndPickupMappings({ + client, + index: 'new_index', + mappings: { properties: {} }, + batchSize: 1000, + query, + }); + try { + await task(); + } catch (e) { + /** ignore */ + } + + expect(pickupUpdatedMappings).toHaveBeenCalledTimes(1); + expect(pickupUpdatedMappings).toHaveBeenCalledWith(client, 'new_index', 1000, query); }); }); }); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/update_and_pickup_mappings.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/update_and_pickup_mappings.ts index 58fd65c9718d0..8478ad08247a5 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/update_and_pickup_mappings.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/update_and_pickup_mappings.ts @@ -11,6 +11,7 @@ import * as TaskEither from 'fp-ts/lib/TaskEither'; import { pipe } from 'fp-ts/lib/pipeable'; import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import type { IndexMapping } from '@kbn/core-saved-objects-base-server-internal'; +import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import { catchRetryableEsClientErrors, type RetryableEsClientError, @@ -29,6 +30,7 @@ export interface UpdateAndPickupMappingsParams { index: string; mappings: IndexMapping; batchSize: number; + query?: QueryDslQueryContainer; } /** * Updates an index's mappings and runs an pickupUpdatedMappings task so that the mapping @@ -39,6 +41,7 @@ export const updateAndPickupMappings = ({ index, mappings, batchSize, + query, }: UpdateAndPickupMappingsParams): TaskEither.TaskEither< RetryableEsClientError, UpdateAndPickupMappingsResponse @@ -76,7 +79,7 @@ export const updateAndPickupMappings = ({ return pipe( putMappingTask, TaskEither.chain((res) => { - return pickupUpdatedMappings(client, index, batchSize); + return pickupUpdatedMappings(client, index, batchSize, query); }) ); }; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.test.ts index 7f1542ffc6008..7139e1e60b584 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.test.ts @@ -10,7 +10,7 @@ import type { IndexMapping, SavedObjectsTypeMappingDefinitions, } from '@kbn/core-saved-objects-base-server-internal'; -import { buildActiveMappings, diffMappings } from './build_active_mappings'; +import { buildActiveMappings, diffMappings, getUpdatedHashes } from './build_active_mappings'; describe('buildActiveMappings', () => { test('creates a strict mapping', () => { @@ -208,3 +208,65 @@ describe('diffMappings', () => { expect(diffMappings(actual, expected)!.changedProp).toEqual('_meta'); }); }); + +describe('getUpdatedHashes', () => { + test('gives all hashes if _meta is missing from actual', () => { + const actual: IndexMapping = { + dynamic: 'strict', + properties: {}, + }; + const expected: IndexMapping = { + _meta: { + migrationMappingPropertyHashes: { foo: 'bar', bar: 'baz' }, + }, + dynamic: 'strict', + properties: {}, + }; + + expect(getUpdatedHashes({ actual, expected })).toEqual(['foo', 'bar']); + }); + + test('gives all hashes if migrationMappingPropertyHashes is missing from actual', () => { + const actual: IndexMapping = { + dynamic: 'strict', + properties: {}, + _meta: {}, + }; + const expected: IndexMapping = { + _meta: { + migrationMappingPropertyHashes: { foo: 'bar', bar: 'baz' }, + }, + dynamic: 'strict', + properties: {}, + }; + + expect(getUpdatedHashes({ actual, expected })).toEqual(['foo', 'bar']); + }); + + test('gives a list of the types with updated hashes', () => { + const actual: IndexMapping = { + dynamic: 'strict', + properties: {}, + _meta: { + migrationMappingPropertyHashes: { + type1: 'type1hash1', + type2: 'type2hash1', + type3: 'type3hash1', // will be removed + }, + }, + }; + const expected: IndexMapping = { + dynamic: 'strict', + properties: {}, + _meta: { + migrationMappingPropertyHashes: { + type1: 'type1hash1', // remains the same + type2: 'type2hash2', // updated + type4: 'type4hash1', // new type + }, + }, + }; + + expect(getUpdatedHashes({ actual, expected })).toEqual(['type2', 'type4']); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.ts index 7dd13acbe8c7f..feec8b5c2aa08 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/core/build_active_mappings.ts @@ -63,6 +63,30 @@ export function diffMappings(actual: IndexMapping, expected: IndexMapping) { return changedProp ? { changedProp: `properties.${changedProp}` } : undefined; } +/** + * Compares the actual vs expected mappings' hashes. + * Returns a list with all the hashes that have been updated. + */ +export const getUpdatedHashes = ({ + actual, + expected, +}: { + actual: IndexMapping; + expected: IndexMapping; +}): string[] => { + if (!actual._meta?.migrationMappingPropertyHashes) { + return Object.keys(expected._meta!.migrationMappingPropertyHashes!); + } + + const updatedHashes = Object.keys(expected._meta!.migrationMappingPropertyHashes!).filter( + (key) => + actual._meta!.migrationMappingPropertyHashes![key] !== + expected._meta!.migrationMappingPropertyHashes![key] + ); + + return updatedHashes; +}; + // Convert an object to an md5 hash string, using a stable serialization (canonicalStringify) function md5Object(obj: any) { return crypto.createHash('md5').update(canonicalStringify(obj)).digest('hex'); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.test.ts index edf426d7be973..382eedfe0c2d8 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.test.ts @@ -7,6 +7,7 @@ */ import { FetchIndexResponse } from '../actions/fetch_indices'; +import { BaseState } from '../state'; import { addExcludedTypesToBoolQuery, addMustClausesToBoolQuery, @@ -20,6 +21,7 @@ import { createBulkIndexOperationTuple, hasLaterVersionAlias, aliasVersion, + getIndexTypes, } from './helpers'; describe('addExcludedTypesToBoolQuery', () => { @@ -444,3 +446,17 @@ describe('getTempIndexName', () => { expect(getTempIndexName('.kibana_cases', '8.8.0')).toEqual('.kibana_cases_8.8.0_reindex_temp'); }); }); + +describe('getIndexTypes', () => { + it("returns the list of types that belong to a migrator's index, based on its state", () => { + const baseState = { + indexPrefix: '.kibana_task_manager', + indexTypesMap: { + '.kibana': ['foo', 'bar'], + '.kibana_task_manager': ['task'], + }, + }; + + expect(getIndexTypes(baseState as unknown as BaseState)).toEqual(['task']); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.ts index d3e8fcabfc432..4588c6639d883 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/helpers.ts @@ -17,7 +17,7 @@ import type { SavedObjectsRawDoc } from '@kbn/core-saved-objects-server'; import type { IndexMapping } from '@kbn/core-saved-objects-base-server-internal'; import type { AliasAction, FetchIndexResponse } from '../actions'; import type { BulkIndexOperationTuple } from './create_batches'; -import { OutdatedDocumentsSearchRead, ReindexSourceToTempRead } from '../state'; +import type { BaseState, OutdatedDocumentsSearchRead, ReindexSourceToTempRead } from '../state'; /** @internal */ export const REINDEX_TEMP_SUFFIX = '_reindex_temp'; @@ -323,3 +323,7 @@ export const increaseBatchSize = ( const increasedBatchSize = Math.floor(stateP.batchSize * 1.2); return increasedBatchSize > stateP.maxBatchSize ? stateP.maxBatchSize : increasedBatchSize; }; + +export const getIndexTypes = (state: BaseState): string[] => { + return state.indexTypesMap[state.indexPrefix]; +}; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.test.ts index 6cb045f6e4d3b..39a03e1e28d47 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.test.ts @@ -2597,19 +2597,75 @@ describe('migrations v2 model', () => { targetIndex: '.kibana_7.11.0_001', }; - it('CHECK_TARGET_MAPPINGS -> UPDATE_TARGET_MAPPINGS_PROPERTIES if mappings do not match', () => { - const res: ResponseType<'CHECK_TARGET_MAPPINGS'> = Either.right({ match: false }); - const newState = model( - checkTargetMappingsState, - res - ) as UpdateTargetMappingsPropertiesState; - expect(newState.controlState).toBe('UPDATE_TARGET_MAPPINGS_PROPERTIES'); + describe('reindex migration', () => { + it('CHECK_TARGET_MAPPINGS -> UPDATE_TARGET_MAPPINGS_PROPERTIES if origin mappings did not exist', () => { + const res: ResponseType<'CHECK_TARGET_MAPPINGS'> = Either.left({ + type: 'actual_mappings_incomplete' as const, + }); + const newState = model( + checkTargetMappingsState, + res + ) as UpdateTargetMappingsPropertiesState; + expect(newState.controlState).toBe('UPDATE_TARGET_MAPPINGS_PROPERTIES'); + expect(Option.isNone(newState.updatedTypesQuery)).toEqual(true); + }); }); - it('CHECK_TARGET_MAPPINGS -> CHECK_VERSION_INDEX_READY_ACTIONS if mappings match', () => { - const res: ResponseType<'CHECK_TARGET_MAPPINGS'> = Either.right({ match: true }); - const newState = model(checkTargetMappingsState, res) as CheckVersionIndexReadyActions; - expect(newState.controlState).toBe('CHECK_VERSION_INDEX_READY_ACTIONS'); + describe('compatible migration', () => { + it('CHECK_TARGET_MAPPINGS -> UPDATE_TARGET_MAPPINGS_PROPERTIES if core fields have been updated', () => { + const res: ResponseType<'CHECK_TARGET_MAPPINGS'> = Either.left({ + type: 'compared_mappings_changed' as const, + updatedRootFields: ['namespaces'], + updatedTypes: ['dashboard', 'lens'], + }); + const newState = model( + checkTargetMappingsState, + res + ) as UpdateTargetMappingsPropertiesState; + expect(newState.controlState).toBe('UPDATE_TARGET_MAPPINGS_PROPERTIES'); + // since a core field has been updated, we must pickup ALL SOs. + // Thus, we must NOT define a filter query. + expect(Option.isNone(newState.updatedTypesQuery)).toEqual(true); + }); + + it('CHECK_TARGET_MAPPINGS -> UPDATE_TARGET_MAPPINGS_PROPERTIES if only SO types have changed', () => { + const res: ResponseType<'CHECK_TARGET_MAPPINGS'> = Either.left({ + type: 'compared_mappings_changed' as const, + updatedRootFields: [], + updatedTypes: ['dashboard', 'lens'], + }); + const newState = model( + checkTargetMappingsState, + res + ) as UpdateTargetMappingsPropertiesState; + expect(newState.controlState).toBe('UPDATE_TARGET_MAPPINGS_PROPERTIES'); + expect( + Option.isSome(newState.updatedTypesQuery) && newState.updatedTypesQuery.value + ).toEqual({ + bool: { + should: [ + { + term: { + type: 'dashboard', + }, + }, + { + term: { + type: 'lens', + }, + }, + ], + }, + }); + }); + + it('CHECK_TARGET_MAPPINGS -> CHECK_VERSION_INDEX_READY_ACTIONS if mappings match', () => { + const res: ResponseType<'CHECK_TARGET_MAPPINGS'> = Either.right({ + type: 'compared_mappings_match' as const, + }); + const newState = model(checkTargetMappingsState, res) as CheckVersionIndexReadyActions; + expect(newState.controlState).toBe('CHECK_VERSION_INDEX_READY_ACTIONS'); + }); }); }); @@ -2842,6 +2898,17 @@ describe('migrations v2 model', () => { versionIndexReadyActions: Option.none, sourceIndex: Option.some('.kibana') as Option.Some, targetIndex: '.kibana_7.11.0_001', + updatedTypesQuery: Option.fromNullable({ + bool: { + should: [ + { + term: { + type: 'type1', + }, + }, + ], + }, + }), }; test('UPDATE_TARGET_MAPPINGS_PROPERTIES -> UPDATE_TARGET_MAPPINGS_PROPERTIES_WAIT_FOR_TASK', () => { const res: ResponseType<'UPDATE_TARGET_MAPPINGS_PROPERTIES'> = Either.right({ diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.ts index ae1098f2209f5..2505581c6bc3d 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/model/model.ts @@ -1422,20 +1422,61 @@ export const model = (currentState: State, resW: ResponseType): } else if (stateP.controlState === 'CHECK_TARGET_MAPPINGS') { const res = resW as ResponseType; if (Either.isRight(res)) { - if (!res.right.match) { - return { - ...stateP, - controlState: 'UPDATE_TARGET_MAPPINGS_PROPERTIES', - }; - } - - // The md5 of the mappings match, so there's no need to update target mappings + // The md5 of ALL mappings match, so there's no need to update target mappings return { ...stateP, controlState: 'CHECK_VERSION_INDEX_READY_ACTIONS', }; } else { - throwBadResponse(stateP, res as never); + const left = res.left; + if (isTypeof(left, 'actual_mappings_incomplete')) { + // reindex migration + // some top-level properties have changed, e.g. 'dynamic' or '_meta' (see checkTargetMappings()) + // we must "pick-up" all documents on the index (by not providing a query) + return { + ...stateP, + controlState: 'UPDATE_TARGET_MAPPINGS_PROPERTIES', + updatedTypesQuery: Option.none, + }; + } else if (isTypeof(left, 'compared_mappings_changed')) { + if (left.updatedRootFields.length) { + // compatible migration: some core fields have been updated + return { + ...stateP, + controlState: 'UPDATE_TARGET_MAPPINGS_PROPERTIES', + // we must "pick-up" all documents on the index (by not providing a query) + updatedTypesQuery: Option.none, + logs: [ + ...stateP.logs, + { + level: 'info', + message: `Kibana is performing a compatible upgrade and the mappings of some root fields have been changed. For Elasticsearch to pickup these mappings, all saved objects need to be updated. Updated root fields: ${left.updatedRootFields}.`, + }, + ], + }; + } else { + // compatible migration: some fields have been updated, and they all correspond to SO types + return { + ...stateP, + controlState: 'UPDATE_TARGET_MAPPINGS_PROPERTIES', + // we can "pick-up" only the SO types that have changed + updatedTypesQuery: Option.some({ + bool: { + should: left.updatedTypes.map((type) => ({ term: { type } })), + }, + }), + logs: [ + ...stateP.logs, + { + level: 'info', + message: `Kibana is performing a compatible upgrade and NO root fields have been udpated. Kibana will update the following SO types so that ES can pickup the updated mappings: ${left.updatedTypes}.`, + }, + ], + }; + } + } else { + throwBadResponse(stateP, res as never); + } } } else if (stateP.controlState === 'UPDATE_TARGET_MAPPINGS_PROPERTIES') { const res = resW as ExcludeRetryableEsError>; diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/next.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/next.ts index 5509fb70c9231..df7e0c23fbc20 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/next.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/next.ts @@ -58,6 +58,7 @@ import { createDelayFn } from './common/utils'; import type { TransformRawDocs } from './types'; import * as Actions from './actions'; import { REMOVED_TYPES } from './core'; +import { getIndexTypes } from './model/helpers'; type ActionMap = ReturnType; @@ -201,6 +202,7 @@ export const nextActionMap = ( Actions.checkTargetMappings({ actualMappings: Option.toUndefined(state.sourceIndexMappings), expectedMappings: state.targetIndexMappings, + indexTypes: getIndexTypes(state), }), UPDATE_TARGET_MAPPINGS_PROPERTIES: (state: UpdateTargetMappingsPropertiesState) => Actions.updateAndPickupMappings({ @@ -208,6 +210,7 @@ export const nextActionMap = ( index: state.targetIndex, mappings: omit(state.targetIndexMappings, ['_meta']), // ._meta property will be updated on a later step batchSize: state.batchSize, + query: Option.toUndefined(state.updatedTypesQuery), }), UPDATE_TARGET_MAPPINGS_PROPERTIES_WAIT_FOR_TASK: ( state: UpdateTargetMappingsPropertiesWaitForTaskState diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/state.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/state.ts index 5dfc5a793046e..d057d464a94d3 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/state.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/state.ts @@ -374,6 +374,7 @@ export interface CheckTargetMappingsState extends PostInitState { export interface UpdateTargetMappingsPropertiesState extends PostInitState { /** Update the mappings of the target index */ readonly controlState: 'UPDATE_TARGET_MAPPINGS_PROPERTIES'; + readonly updatedTypesQuery: Option.Option; } export interface UpdateTargetMappingsPropertiesWaitForTaskState extends PostInitState { diff --git a/packages/deeplinks/management/deep_links.ts b/packages/deeplinks/management/deep_links.ts index 1fa3d0f7d30bd..0e9b16dd7dc8f 100644 --- a/packages/deeplinks/management/deep_links.ts +++ b/packages/deeplinks/management/deep_links.ts @@ -54,4 +54,8 @@ export type ManagementDeepLinkId = MonitoringAppId | `${ManagementAppId}:${Manag // Combined export type AppId = MonitoringAppId | IntegrationsAppId | ManagementAppId; export type LinkId = ManagementId; -export type DeepLinkId = MonitoringDeepLinkId | IntegrationsDeepLinkId | ManagementDeepLinkId; +export type DeepLinkId = + | AppId + | MonitoringDeepLinkId + | IntegrationsDeepLinkId + | ManagementDeepLinkId; diff --git a/packages/default-nav/ml/default_navigation.ts b/packages/default-nav/ml/default_navigation.ts index 166307439ca8f..5eb99482fd68c 100644 --- a/packages/default-nav/ml/default_navigation.ts +++ b/packages/default-nav/ml/default_navigation.ts @@ -23,9 +23,9 @@ export type MlNodeDefinition = NodeDefinitionWithChildren true); jest.mock('copy-to-clipboard', () => (text: string) => mockCopy(text)); describe('Default createCopyToClipboardActionFactory', () => { const copyToClipboardActionFactory = createCopyToClipboardActionFactory({ - notifications: { toasts: { addSuccess: mockSuccessToast } } as unknown as NotificationsStart, + notifications: { + toasts: { addSuccess: mockSuccessToast, addWarning: mockWarningToast }, + } as unknown as NotificationsStart, }); const copyToClipboardAction = copyToClipboardActionFactory({ id: 'testAction' }); const context = { data: [ { - field: { name: 'user.name', type: 'text' }, + field: { name: 'user.name', type: 'string' }, value: 'the value', }, ], @@ -45,6 +49,20 @@ describe('Default createCopyToClipboardActionFactory', () => { it('should return true if everything is okay', async () => { expect(await copyToClipboardAction.isCompatible(context)).toEqual(true); }); + + it('should return false if Kbn type is unsupported', async () => { + expect( + await copyToClipboardAction.isCompatible({ + ...context, + data: [ + { + ...context.data[0], + field: { ...context.data[0].field, type: KBN_FIELD_TYPES.NUMBER_RANGE }, + }, + ], + }) + ).toEqual(false); + }); }); describe('execute', () => { @@ -111,5 +129,19 @@ describe('Default createCopyToClipboardActionFactory', () => { expect(mockCopy).toHaveBeenCalledWith('user.name: true AND false AND true'); expect(mockSuccessToast).toHaveBeenCalled(); }); + + it('should notify the user when value type is unsupported', async () => { + await copyToClipboardAction.execute({ + ...context, + data: [ + { + ...context.data[0], + value: {}, + }, + ], + }); + expect(mockCopy).not.toHaveBeenCalled(); + expect(mockWarningToast).toHaveBeenCalled(); + }); }); }); diff --git a/packages/kbn-cell-actions/src/actions/copy_to_clipboard/copy_to_clipboard.ts b/packages/kbn-cell-actions/src/actions/copy_to_clipboard/copy_to_clipboard.ts index 80cb7dd19c0b3..8805b02ed9cfc 100644 --- a/packages/kbn-cell-actions/src/actions/copy_to_clipboard/copy_to_clipboard.ts +++ b/packages/kbn-cell-actions/src/actions/copy_to_clipboard/copy_to_clipboard.ts @@ -10,8 +10,16 @@ import copy from 'copy-to-clipboard'; import { i18n } from '@kbn/i18n'; import type { NotificationsStart } from '@kbn/core/public'; import { isString } from 'lodash/fp'; +import { KBN_FIELD_TYPES } from '@kbn/field-types'; import { COPY_CELL_ACTION_TYPE } from '../../constants'; import { createCellActionFactory } from '../factory'; +import { + filterOutNullableValues, + isTypeSupportedByDefaultActions, + isValueSupportedByDefaultActions, + valueToArray, +} from '../utils'; +import { ACTION_INCOMPATIBLE_VALUE_WARNING } from '../translations'; const ICON = 'copyClipboard'; const COPY_TO_CLIPBOARD = i18n.translate('cellActions.actions.copyToClipboard.displayName', { @@ -37,19 +45,24 @@ export const createCopyToClipboardActionFactory = createCellActionFactory( return ( data.length === 1 && // TODO Add support for multiple values - field.name != null + field.name != null && + isTypeSupportedByDefaultActions(field.type as KBN_FIELD_TYPES) ); }, execute: async ({ data }) => { const field = data[0]?.field; - const value = data[0]?.value; + const rawValue = data[0]?.value; + const value = filterOutNullableValues(valueToArray(rawValue)); - let textValue: undefined | string; - if (value != null) { - const valuesArray = Array.isArray(value) ? value : [value]; - textValue = valuesArray.map((v) => (isString(v) ? `"${escapeValue(v)}"` : v)).join(' AND '); + if (!isValueSupportedByDefaultActions(value)) { + notifications.toasts.addWarning({ + title: ACTION_INCOMPATIBLE_VALUE_WARNING, + }); + return; } - const text = textValue ? `${field.name}: ${textValue}` : field.name; + + const textValue = value.map((v) => (isString(v) ? `"${escapeValue(v)}"` : v)).join(' AND '); + const text = textValue !== '' ? `${field.name}: ${textValue}` : field.name; const isSuccess = copy(text, { debug: true }); if (isSuccess) { diff --git a/packages/kbn-cell-actions/src/actions/filter/create_filter.test.ts b/packages/kbn-cell-actions/src/actions/filter/create_filter.test.ts index 846d30a237fcf..d38b25f93e929 100644 --- a/packages/kbn-cell-actions/src/actions/filter/create_filter.test.ts +++ b/packages/kbn-cell-actions/src/actions/filter/create_filter.test.ts @@ -15,11 +15,8 @@ const booleanValue = true; describe('createFilter', () => { it.each([ - { caseName: 'string', caseValue: value }, { caseName: 'string array', caseValue: [value] }, - { caseName: 'number', caseValue: numberValue, query: numberValue.toString() }, { caseName: 'number array', caseValue: [numberValue], query: numberValue.toString() }, - { caseName: 'boolean', caseValue: booleanValue, query: booleanValue.toString() }, { caseName: 'boolean array', caseValue: [booleanValue], query: booleanValue.toString() }, ])('should return filter with $caseName value', ({ caseValue, query = value }) => { expect(createFilter({ key: field, value: caseValue, negate: false })).toEqual({ @@ -42,11 +39,8 @@ describe('createFilter', () => { }); it.each([ - { caseName: 'string', caseValue: value }, { caseName: 'string array', caseValue: [value] }, - { caseName: 'number', caseValue: numberValue, query: numberValue.toString() }, { caseName: 'number array', caseValue: [numberValue], query: numberValue.toString() }, - { caseName: 'boolean', caseValue: booleanValue, query: booleanValue.toString() }, { caseName: 'boolean array', caseValue: [booleanValue], query: booleanValue.toString() }, ])('should return negate filter with $caseName value', ({ caseValue, query = value }) => { expect(createFilter({ key: field, value: caseValue, negate: true })).toEqual({ @@ -93,45 +87,41 @@ describe('createFilter', () => { }); }); - it.each([ - { caseName: 'null', caseValue: null }, - { caseName: 'undefined', caseValue: undefined }, - { caseName: 'empty string', caseValue: '' }, - { caseName: 'empty array', caseValue: [] }, - ])('should return exist filter with $caseName value', ({ caseValue }) => { - expect(createFilter({ key: field, value: caseValue, negate: false })).toEqual({ - query: { - exists: { - field, + it.each([{ caseName: 'empty array', caseValue: [] }])( + 'should return exist filter with $caseName value', + ({ caseValue }) => { + expect(createFilter({ key: field, value: caseValue, negate: false })).toEqual({ + query: { + exists: { + field, + }, }, - }, - meta: { - key: field, - negate: false, - type: 'exists', - value: 'exists', - }, - }); - }); + meta: { + key: field, + negate: false, + type: 'exists', + value: 'exists', + }, + }); + } + ); - it.each([ - { caseName: 'null', caseValue: null }, - { caseName: 'undefined', caseValue: undefined }, - { caseName: 'empty string', caseValue: '' }, - { caseName: 'empty array', caseValue: [] }, - ])('should return negate exist filter with $caseName value', ({ caseValue }) => { - expect(createFilter({ key: field, value: caseValue, negate: true })).toEqual({ - query: { - exists: { - field, + it.each([{ caseName: 'empty array', caseValue: [] }])( + 'should return negate exist filter with $caseName value', + ({ caseValue }) => { + expect(createFilter({ key: field, value: caseValue, negate: true })).toEqual({ + query: { + exists: { + field, + }, }, - }, - meta: { - key: field, - negate: true, - type: 'exists', - value: 'exists', - }, - }); - }); + meta: { + key: field, + negate: true, + type: 'exists', + value: 'exists', + }, + }); + } + ); }); diff --git a/packages/kbn-cell-actions/src/actions/filter/create_filter.ts b/packages/kbn-cell-actions/src/actions/filter/create_filter.ts index 483d7709aebd5..8f0fee5a5fa03 100644 --- a/packages/kbn-cell-actions/src/actions/filter/create_filter.ts +++ b/packages/kbn-cell-actions/src/actions/filter/create_filter.ts @@ -13,13 +13,10 @@ import { type PhraseFilter, type Filter, } from '@kbn/es-query'; -import { isArray } from 'lodash/fp'; -import { CellActionFieldValue } from '../../types'; +import { DefaultActionsSupportedValue } from '../types'; -export const isEmptyFilterValue = ( - value: CellActionFieldValue -): value is null | undefined | never[] => - value == null || value === '' || (isArray(value) && value.length === 0); +export const isEmptyFilterValue = (value: Array) => + value.length === 0 || value.every((v) => v === ''); const createExistsFilter = ({ key, negate }: { key: string; negate: boolean }): ExistsFilter => ({ meta: { key, negate, type: FILTERS.EXISTS, value: 'exists' }, @@ -49,7 +46,7 @@ const createCombinedFilter = ({ key, negate, }: { - values: string[] | number[] | boolean[]; + values: DefaultActionsSupportedValue; key: string; negate: boolean; }): CombinedFilter => ({ @@ -68,18 +65,16 @@ export const createFilter = ({ negate, }: { key: string; - value: CellActionFieldValue; + value: DefaultActionsSupportedValue; negate: boolean; }): Filter => { - if (isEmptyFilterValue(value)) { + if (value.length === 0) { return createExistsFilter({ key, negate }); } - if (Array.isArray(value)) { - if (value.length > 1) { - return createCombinedFilter({ key, negate, values: value }); - } else { - return createPhraseFilter({ key, negate, value: value[0] }); - } + + if (value.length > 1) { + return createCombinedFilter({ key, negate, values: value }); + } else { + return createPhraseFilter({ key, negate, value: value[0] }); } - return createPhraseFilter({ key, negate, value }); }; diff --git a/packages/kbn-cell-actions/src/actions/filter/filter_in.test.ts b/packages/kbn-cell-actions/src/actions/filter/filter_in.test.ts index 546881072aed9..b1006e21c01f5 100644 --- a/packages/kbn-cell-actions/src/actions/filter/filter_in.test.ts +++ b/packages/kbn-cell-actions/src/actions/filter/filter_in.test.ts @@ -5,9 +5,10 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import type { FilterManager } from '@kbn/data-plugin/public'; +import { FilterManager, KBN_FIELD_TYPES } from '@kbn/data-plugin/public'; import { createFilterInActionFactory } from './filter_in'; import { makeActionContext } from '../../mocks/helpers'; +import { NotificationsStart } from '@kbn/core-notifications-browser'; const mockFilterManager = { addFilters: jest.fn() } as unknown as FilterManager; @@ -20,15 +21,18 @@ jest.mock('./create_filter', () => ({ const fieldName = 'user.name'; const value = 'the value'; +const mockWarningToast = jest.fn(); + describe('createFilterInActionFactory', () => { const filterInActionFactory = createFilterInActionFactory({ filterManager: mockFilterManager, + notifications: { toasts: { addWarning: mockWarningToast } } as unknown as NotificationsStart, }); const filterInAction = filterInActionFactory({ id: 'testAction' }); const context = makeActionContext({ data: [ { - field: { name: fieldName, type: 'text', searchable: true, aggregatable: true }, + field: { name: fieldName, type: 'string', searchable: true, aggregatable: true }, value, }, ], @@ -57,12 +61,27 @@ describe('createFilterInActionFactory', () => { ...context, data: [ { + ...context.data[0], field: { ...context.data[0].field, name: '' }, }, ], }) ).toEqual(false); }); + + it('should return false if Kbn type is unsupported', async () => { + expect( + await filterInAction.isCompatible({ + ...context, + data: [ + { + ...context.data[0], + field: { ...context.data[0].field, type: KBN_FIELD_TYPES.MISSING }, + }, + ], + }) + ).toEqual(false); + }); }); describe('execute', () => { @@ -75,7 +94,7 @@ describe('createFilterInActionFactory', () => { await filterInAction.execute(context); expect(mockCreateFilter).toHaveBeenCalledWith({ key: fieldName, - value, + value: [value], negate: false, }); }); @@ -107,7 +126,7 @@ describe('createFilterInActionFactory', () => { }, ], }); - expect(mockCreateFilter).toHaveBeenCalledWith({ key: fieldName, value: null, negate: true }); + expect(mockCreateFilter).toHaveBeenCalledWith({ key: fieldName, value: [], negate: true }); }); it('should create negate filter query with undefined value', async () => { @@ -122,7 +141,7 @@ describe('createFilterInActionFactory', () => { }); expect(mockCreateFilter).toHaveBeenCalledWith({ key: fieldName, - value: undefined, + value: [], negate: true, }); }); @@ -137,7 +156,7 @@ describe('createFilterInActionFactory', () => { }, ], }); - expect(mockCreateFilter).toHaveBeenCalledWith({ key: fieldName, value: '', negate: true }); + expect(mockCreateFilter).toHaveBeenCalledWith({ key: fieldName, value: [''], negate: true }); }); it('should create negate filter query with empty array value', async () => { @@ -152,5 +171,19 @@ describe('createFilterInActionFactory', () => { }); expect(mockCreateFilter).toHaveBeenCalledWith({ key: fieldName, value: [], negate: true }); }); + + it('should notify the user when value type is unsupported', async () => { + await filterInAction.execute({ + ...context, + data: [ + { + ...context.data[0], + value: [{}, {}, {}], + }, + ], + }); + expect(mockCreateFilter).not.toHaveBeenCalled(); + expect(mockWarningToast).toHaveBeenCalled(); + }); }); }); diff --git a/packages/kbn-cell-actions/src/actions/filter/filter_in.ts b/packages/kbn-cell-actions/src/actions/filter/filter_in.ts index 91ca6cdcf5c9e..419d25079a704 100644 --- a/packages/kbn-cell-actions/src/actions/filter/filter_in.ts +++ b/packages/kbn-cell-actions/src/actions/filter/filter_in.ts @@ -6,11 +6,19 @@ * Side Public License, v 1. */ import { i18n } from '@kbn/i18n'; -import type { FilterManager } from '@kbn/data-plugin/public'; +import type { FilterManager, KBN_FIELD_TYPES } from '@kbn/data-plugin/public'; +import { NotificationsStart } from '@kbn/core-notifications-browser'; import { createFilter, isEmptyFilterValue } from './create_filter'; import { FILTER_CELL_ACTION_TYPE } from '../../constants'; import { createCellActionFactory } from '../factory'; -import { CellActionFieldValue } from '../../types'; +import { + filterOutNullableValues, + isTypeSupportedByDefaultActions, + isValueSupportedByDefaultActions, + valueToArray, +} from '../utils'; +import { ACTION_INCOMPATIBLE_VALUE_WARNING } from '../translations'; +import { DefaultActionsSupportedValue } from '../types'; const ICON = 'plusInCircle'; const FILTER_IN = i18n.translate('cellActions.actions.filterIn', { @@ -18,7 +26,13 @@ const FILTER_IN = i18n.translate('cellActions.actions.filterIn', { }); export const createFilterInActionFactory = createCellActionFactory( - ({ filterManager }: { filterManager: FilterManager }) => ({ + ({ + filterManager, + notifications: { toasts }, + }: { + filterManager: FilterManager; + notifications: NotificationsStart; + }) => ({ type: FILTER_CELL_ACTION_TYPE, getIconType: () => ICON, getDisplayName: () => FILTER_IN, @@ -28,13 +42,22 @@ export const createFilterInActionFactory = createCellActionFactory( return ( data.length === 1 && // TODO Add support for multiple values - !!field.name + !!field.name && + isTypeSupportedByDefaultActions(field.type as KBN_FIELD_TYPES) ); }, execute: async ({ data }) => { const field = data[0]?.field; - const value = data[0]?.value; - addFilterIn({ filterManager, fieldName: field.name, value }); + const rawValue = data[0]?.value; + const value = filterOutNullableValues(valueToArray(rawValue)); + + if (isValueSupportedByDefaultActions(value)) { + addFilterIn({ filterManager, fieldName: field.name, value }); + } else { + toasts.addWarning({ + title: ACTION_INCOMPATIBLE_VALUE_WARNING, + }); + } }, }) ); @@ -46,7 +69,7 @@ export const addFilterIn = ({ }: { filterManager: FilterManager | undefined; fieldName: string; - value: CellActionFieldValue; + value: DefaultActionsSupportedValue; }) => { if (filterManager != null) { const filter = createFilter({ diff --git a/packages/kbn-cell-actions/src/actions/filter/filter_out.test.ts b/packages/kbn-cell-actions/src/actions/filter/filter_out.test.ts index d42b8bc7a26a4..2d56a93ee420e 100644 --- a/packages/kbn-cell-actions/src/actions/filter/filter_out.test.ts +++ b/packages/kbn-cell-actions/src/actions/filter/filter_out.test.ts @@ -5,9 +5,10 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import type { FilterManager } from '@kbn/data-plugin/public'; +import { FilterManager, KBN_FIELD_TYPES } from '@kbn/data-plugin/public'; import { createFilterOutActionFactory } from './filter_out'; import { makeActionContext } from '../../mocks/helpers'; +import { NotificationsStart } from '@kbn/core-notifications-browser'; const mockFilterManager = { addFilters: jest.fn() } as unknown as FilterManager; @@ -20,13 +21,18 @@ jest.mock('./create_filter', () => ({ const fieldName = 'user.name'; const value = 'the value'; +const mockWarningToast = jest.fn(); + describe('createFilterOutAction', () => { - const filterOutActionFactory = createFilterOutActionFactory({ filterManager: mockFilterManager }); + const filterOutActionFactory = createFilterOutActionFactory({ + filterManager: mockFilterManager, + notifications: { toasts: { addWarning: mockWarningToast } } as unknown as NotificationsStart, + }); const filterOutAction = filterOutActionFactory({ id: 'testAction' }); const context = makeActionContext({ data: [ { - field: { name: fieldName, type: 'text', searchable: true, aggregatable: true }, + field: { name: fieldName, type: 'string', searchable: true, aggregatable: true }, value, }, ], @@ -61,6 +67,20 @@ describe('createFilterOutAction', () => { }) ).toEqual(false); }); + + it('should return false if Kbn type is unsupported', async () => { + expect( + await filterOutAction.isCompatible({ + ...context, + data: [ + { + ...context.data[0], + field: { ...context.data[0].field, type: KBN_FIELD_TYPES._SOURCE }, + }, + ], + }) + ).toEqual(false); + }); }); describe('execute', () => { @@ -71,7 +91,11 @@ describe('createFilterOutAction', () => { it('should create negate filter query with value', async () => { await filterOutAction.execute(context); - expect(mockCreateFilter).toHaveBeenCalledWith({ key: fieldName, value, negate: true }); + expect(mockCreateFilter).toHaveBeenCalledWith({ + key: fieldName, + value: [value], + negate: true, + }); }); it('should create negate filter query with array value', async () => { @@ -101,7 +125,7 @@ describe('createFilterOutAction', () => { }, ], }); - expect(mockCreateFilter).toHaveBeenCalledWith({ key: fieldName, value: null, negate: false }); + expect(mockCreateFilter).toHaveBeenCalledWith({ key: fieldName, value: [], negate: false }); }); it('should create filter query with undefined value', async () => { @@ -116,7 +140,7 @@ describe('createFilterOutAction', () => { }); expect(mockCreateFilter).toHaveBeenCalledWith({ key: fieldName, - value: undefined, + value: [], negate: false, }); }); @@ -131,7 +155,7 @@ describe('createFilterOutAction', () => { }, ], }); - expect(mockCreateFilter).toHaveBeenCalledWith({ key: fieldName, value: '', negate: false }); + expect(mockCreateFilter).toHaveBeenCalledWith({ key: fieldName, value: [''], negate: false }); }); it('should create negate filter query with empty array value', async () => { @@ -146,5 +170,19 @@ describe('createFilterOutAction', () => { }); expect(mockCreateFilter).toHaveBeenCalledWith({ key: fieldName, value: [], negate: false }); }); + + it('should notify the user when value type is unsupported', async () => { + await filterOutAction.execute({ + ...context, + data: [ + { + ...context.data[0], + value: { a: {} }, + }, + ], + }); + expect(mockCreateFilter).not.toHaveBeenCalled(); + expect(mockWarningToast).toHaveBeenCalled(); + }); }); }); diff --git a/packages/kbn-cell-actions/src/actions/filter/filter_out.ts b/packages/kbn-cell-actions/src/actions/filter/filter_out.ts index 2a4622de97286..237a4f11746af 100644 --- a/packages/kbn-cell-actions/src/actions/filter/filter_out.ts +++ b/packages/kbn-cell-actions/src/actions/filter/filter_out.ts @@ -6,11 +6,19 @@ * Side Public License, v 1. */ import { i18n } from '@kbn/i18n'; -import type { FilterManager } from '@kbn/data-plugin/public'; +import type { FilterManager, KBN_FIELD_TYPES } from '@kbn/data-plugin/public'; +import { NotificationsStart } from '@kbn/core-notifications-browser'; import { createFilter, isEmptyFilterValue } from './create_filter'; import { FILTER_CELL_ACTION_TYPE } from '../../constants'; import { createCellActionFactory } from '../factory'; -import { CellActionFieldValue } from '../../types'; +import { + isTypeSupportedByDefaultActions, + isValueSupportedByDefaultActions, + valueToArray, + filterOutNullableValues, +} from '../utils'; +import { ACTION_INCOMPATIBLE_VALUE_WARNING } from '../translations'; +import { DefaultActionsSupportedValue } from '../types'; const ICON = 'minusInCircle'; const FILTER_OUT = i18n.translate('cellActions.actions.filterOut', { @@ -18,7 +26,13 @@ const FILTER_OUT = i18n.translate('cellActions.actions.filterOut', { }); export const createFilterOutActionFactory = createCellActionFactory( - ({ filterManager }: { filterManager: FilterManager }) => ({ + ({ + filterManager, + notifications: { toasts }, + }: { + filterManager: FilterManager; + notifications: NotificationsStart; + }) => ({ type: FILTER_CELL_ACTION_TYPE, getIconType: () => ICON, getDisplayName: () => FILTER_OUT, @@ -28,18 +42,27 @@ export const createFilterOutActionFactory = createCellActionFactory( return ( data.length === 1 && // TODO Add support for multiple values - !!field.name + !!field.name && + isTypeSupportedByDefaultActions(field.type as KBN_FIELD_TYPES) ); }, + execute: async ({ data }) => { const field = data[0]?.field; - const value = data[0]?.value; + const rawValue = data[0]?.value; + const value = filterOutNullableValues(valueToArray(rawValue)); - addFilterOut({ - filterManager, - fieldName: field.name, - value, - }); + if (isValueSupportedByDefaultActions(value)) { + addFilterOut({ + filterManager, + fieldName: field.name, + value, + }); + } else { + toasts.addWarning({ + title: ACTION_INCOMPATIBLE_VALUE_WARNING, + }); + } }, }) ); @@ -51,7 +74,7 @@ export const addFilterOut = ({ }: { filterManager: FilterManager | undefined; fieldName: string; - value: CellActionFieldValue; + value: DefaultActionsSupportedValue; }) => { if (filterManager != null) { const filter = createFilter({ diff --git a/packages/kbn-cell-actions/src/actions/translations.ts b/packages/kbn-cell-actions/src/actions/translations.ts new file mode 100644 index 0000000000000..a8e9828959eee --- /dev/null +++ b/packages/kbn-cell-actions/src/actions/translations.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; + +export const ACTION_INCOMPATIBLE_VALUE_WARNING = i18n.translate( + 'cellActions.actions.incompatibility.warningMessage', + { + defaultMessage: 'The action can not be executed because the value and type are incompatible', + } +); diff --git a/packages/kbn-cell-actions/src/actions/types.ts b/packages/kbn-cell-actions/src/actions/types.ts new file mode 100644 index 0000000000000..f7700e3c5edaf --- /dev/null +++ b/packages/kbn-cell-actions/src/actions/types.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 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 { SerializableRecord } from '@kbn/utility-types'; +import { SerializableArray } from '@kbn/utility-types/src/serializable'; + +export type DefaultActionsSupportedValue = string[] | number[] | boolean[]; + +export type NonNullableSerializable = + | string + | number + | boolean + | SerializableArray + | SerializableRecord; diff --git a/packages/kbn-cell-actions/src/actions/utils.test.ts b/packages/kbn-cell-actions/src/actions/utils.test.ts new file mode 100644 index 0000000000000..28ad6e8bd02d2 --- /dev/null +++ b/packages/kbn-cell-actions/src/actions/utils.test.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { KBN_FIELD_TYPES } from '@kbn/field-types'; +import { + filterOutNullableValues, + isTypeSupportedByDefaultActions, + isValueSupportedByDefaultActions, +} from './utils'; + +describe('utils', () => { + describe('isTypeSupportedByDefaultActions', () => { + it('returns true when the type is number', () => { + expect(isTypeSupportedByDefaultActions(KBN_FIELD_TYPES.NUMBER)).toBe(true); + }); + + it('returns true when the type is string', () => { + expect(isTypeSupportedByDefaultActions(KBN_FIELD_TYPES.STRING)).toBe(true); + }); + + it('returns true when the type is ip', () => { + expect(isTypeSupportedByDefaultActions(KBN_FIELD_TYPES.IP)).toBe(true); + }); + + it('returns true when the type is date', () => { + expect(isTypeSupportedByDefaultActions(KBN_FIELD_TYPES.DATE)).toBe(true); + }); + + it('returns true when the type is boolean', () => { + expect(isTypeSupportedByDefaultActions(KBN_FIELD_TYPES.BOOLEAN)).toBe(true); + }); + + it('returns false when the type is unknown', () => { + expect(isTypeSupportedByDefaultActions(KBN_FIELD_TYPES.UNKNOWN)).toBe(false); + }); + }); + + describe('isValueSupportedByDefaultActions', () => { + it('returns true when the value is an array of strings', () => { + expect(isValueSupportedByDefaultActions(['string', 'string'])).toBe(true); + }); + + it('returns true when the value is an array of number', () => { + expect(isValueSupportedByDefaultActions([2, 2])).toBe(true); + }); + + it('returns true when the value is an empty array', () => { + expect(isValueSupportedByDefaultActions([])).toBe(true); + }); + + it('returns true when the value is an array of booleans', () => { + expect(isValueSupportedByDefaultActions([false, true])).toBe(true); + }); + + it('returns false when the value is an mixed-type array', () => { + expect(isValueSupportedByDefaultActions([2, 'string', false])).toBe(false); + }); + }); + + describe('filterOutNullableValues', () => { + it('returns empty array when all values are nullable', () => { + expect(filterOutNullableValues([null, undefined, null, undefined])).toEqual([]); + }); + + it('returns the same elements when they are all non-nullable', () => { + expect(filterOutNullableValues([2, 'string', true])).toEqual([2, 'string', true]); + }); + }); +}); diff --git a/packages/kbn-cell-actions/src/actions/utils.ts b/packages/kbn-cell-actions/src/actions/utils.ts new file mode 100644 index 0000000000000..6282f6a7772e3 --- /dev/null +++ b/packages/kbn-cell-actions/src/actions/utils.ts @@ -0,0 +1,39 @@ +/* + * Copyright 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 { KBN_FIELD_TYPES } from '@kbn/field-types'; +import { isBoolean, isNumber, isString } from 'lodash/fp'; +import { Serializable, SerializableArray } from '@kbn/utility-types/src/serializable'; +import { DefaultActionsSupportedValue, NonNullableSerializable } from './types'; + +export const SUPPORTED_KBN_TYPES = [ + KBN_FIELD_TYPES.DATE, + KBN_FIELD_TYPES.IP, + KBN_FIELD_TYPES.STRING, + KBN_FIELD_TYPES.NUMBER, + KBN_FIELD_TYPES.BOOLEAN, +]; + +export const isTypeSupportedByDefaultActions = (kbnFieldType: KBN_FIELD_TYPES) => + SUPPORTED_KBN_TYPES.includes(kbnFieldType); + +const isNonMixedTypeArray = ( + value: Array +): value is string[] | number[] | boolean[] => value.every((v) => typeof v === typeof value[0]); + +export const isValueSupportedByDefaultActions = ( + value: NonNullableSerializable[] +): value is DefaultActionsSupportedValue => + value.every((v): v is string | number | boolean => isString(v) || isNumber(v) || isBoolean(v)) && + isNonMixedTypeArray(value); + +export const filterOutNullableValues = (value: SerializableArray): NonNullableSerializable[] => + value.filter((v): v is NonNullableSerializable => v != null); + +export const valueToArray = (value: Serializable): SerializableArray => + Array.isArray(value) ? value : [value]; diff --git a/packages/kbn-cell-actions/src/constants.ts b/packages/kbn-cell-actions/src/constants.ts index 4e6b1d0f36235..70b9862a933de 100644 --- a/packages/kbn-cell-actions/src/constants.ts +++ b/packages/kbn-cell-actions/src/constants.ts @@ -6,8 +6,6 @@ * Side Public License, v 1. */ -import { KBN_FIELD_TYPES } from '@kbn/field-types'; - export const FILTER_CELL_ACTION_TYPE = 'cellAction-filter'; export const COPY_CELL_ACTION_TYPE = 'cellAction-copy'; @@ -16,11 +14,3 @@ export enum CellActionsMode { HOVER_RIGHT = 'hover-right', INLINE = 'inline', } - -export const SUPPORTED_KBN_TYPES = [ - KBN_FIELD_TYPES.DATE, - KBN_FIELD_TYPES.IP, - KBN_FIELD_TYPES.STRING, - KBN_FIELD_TYPES.NUMBER, - KBN_FIELD_TYPES.BOOLEAN, -]; diff --git a/packages/kbn-cell-actions/src/types.ts b/packages/kbn-cell-actions/src/types.ts index 142144b13b72a..bd7cb69c38aac 100644 --- a/packages/kbn-cell-actions/src/types.ts +++ b/packages/kbn-cell-actions/src/types.ts @@ -11,6 +11,7 @@ import type { UiActionsService, } from '@kbn/ui-actions-plugin/public'; import type { FieldSpec } from '@kbn/data-views-plugin/common'; +import { Serializable } from '@kbn/utility-types'; import type { CellActionsMode } from './constants'; export interface CellActionsProviderProps { @@ -24,14 +25,14 @@ export interface CellActionsProviderProps { type Metadata = Record; export type CellActionFieldValue = - | string - | number - | boolean + | Serializable + // Add primitive array types to allow type guards to work. + // Because SerializableArray is a cyclic self referenced Array. | string[] | number[] | boolean[] - | null - | undefined; + | null[] + | undefined[]; export interface CellActionsData { /** diff --git a/packages/kbn-cell-actions/src/utils.test.ts b/packages/kbn-cell-actions/src/utils.test.ts deleted file mode 100644 index 36cf1dfa4cccb..0000000000000 --- a/packages/kbn-cell-actions/src/utils.test.ts +++ /dev/null @@ -1,36 +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 { KBN_FIELD_TYPES } from '@kbn/field-types'; -import { isTypeSupportedByCellActions } from './utils'; - -describe('isTypeSupportedByCellActions', () => { - it('returns true if the type is number', () => { - expect(isTypeSupportedByCellActions(KBN_FIELD_TYPES.NUMBER)).toBe(true); - }); - - it('returns true if the type is string', () => { - expect(isTypeSupportedByCellActions(KBN_FIELD_TYPES.STRING)).toBe(true); - }); - - it('returns true if the type is ip', () => { - expect(isTypeSupportedByCellActions(KBN_FIELD_TYPES.IP)).toBe(true); - }); - - it('returns true if the type is date', () => { - expect(isTypeSupportedByCellActions(KBN_FIELD_TYPES.DATE)).toBe(true); - }); - - it('returns true if the type is boolean', () => { - expect(isTypeSupportedByCellActions(KBN_FIELD_TYPES.BOOLEAN)).toBe(true); - }); - - it('returns false if the type is unknown', () => { - expect(isTypeSupportedByCellActions(KBN_FIELD_TYPES.UNKNOWN)).toBe(false); - }); -}); diff --git a/packages/kbn-cell-actions/tsconfig.json b/packages/kbn-cell-actions/tsconfig.json index c504b32bac30d..f7ac4890347af 100644 --- a/packages/kbn-cell-actions/tsconfig.json +++ b/packages/kbn-cell-actions/tsconfig.json @@ -21,6 +21,8 @@ "@kbn/ui-actions-plugin", "@kbn/field-types", "@kbn/data-views-plugin", + "@kbn/core-notifications-browser", + "@kbn/utility-types", ], "exclude": ["target/**/*"] } diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index 959308de9d5fe..b47b0ab86ac3b 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -20,6 +20,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { const DOC_LINK_VERSION = meta.version; const ELASTIC_WEBSITE_URL = meta.elasticWebsiteUrl; const DOCS_WEBSITE_URL = meta.docsWebsiteUrl; + const ELASTIC_GITHUB = meta.elasticGithubUrl; const ELASTICSEARCH_DOCS = `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}/`; const KIBANA_DOCS = `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/`; @@ -305,6 +306,9 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { }, addData: `${KIBANA_DOCS}connect-to-elasticsearch.html`, kibana: { + askElastic: `${ELASTIC_WEBSITE_URL}products/kibana/ask-elastic?blade=kibanaaskelastic`, + createGithubIssue: `${ELASTIC_GITHUB}kibana/issues/new/choose`, + feedback: `${ELASTIC_WEBSITE_URL}products/kibana/feedback?blade=kibanafeedback`, guide: `${KIBANA_DOCS}index.html`, autocompleteSuggestions: `${KIBANA_DOCS}kibana-concepts-analysts.html#autocomplete-suggestions`, secureSavedObject: `${KIBANA_DOCS}xpack-security-secure-saved-objects.html`, diff --git a/packages/kbn-doc-links/src/get_doc_meta.ts b/packages/kbn-doc-links/src/get_doc_meta.ts index e433b2acf1f04..46bd5fb0f06cf 100644 --- a/packages/kbn-doc-links/src/get_doc_meta.ts +++ b/packages/kbn-doc-links/src/get_doc_meta.ts @@ -16,6 +16,7 @@ export const getDocLinksMeta = ({ kibanaBranch }: GetDocLinksMetaOptions): DocLi return { version: kibanaBranch === 'main' ? 'master' : kibanaBranch, elasticWebsiteUrl: 'https://www.elastic.co/', + elasticGithubUrl: 'https://github.com/elastic/', docsWebsiteUrl: 'https://docs.elastic.co/', }; }; diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index f99515b2d214e..5f6b92ebd2a44 100644 --- a/packages/kbn-doc-links/src/types.ts +++ b/packages/kbn-doc-links/src/types.ts @@ -12,6 +12,7 @@ export interface DocLinksMeta { version: string; elasticWebsiteUrl: string; + elasticGithubUrl: string; docsWebsiteUrl: string; } @@ -284,6 +285,9 @@ export interface DocLinks { }; readonly addData: string; readonly kibana: { + readonly askElastic: string; + readonly createGithubIssue: string; + readonly feedback: string; readonly guide: string; readonly autocompleteSuggestions: string; readonly secureSavedObject: string; diff --git a/packages/kbn-es/src/utils/extract_config_files.test.js b/packages/kbn-es/src/utils/extract_config_files.test.js index a78dc8111707f..313a8c68b0194 100644 --- a/packages/kbn-es/src/utils/extract_config_files.test.js +++ b/packages/kbn-es/src/utils/extract_config_files.test.js @@ -10,6 +10,11 @@ jest.mock('fs', () => ({ readFileSync: jest.fn(), existsSync: jest.fn().mockImplementation(() => true), writeFileSync: jest.fn(), + statSync: jest.fn((fileName) => { + return { + isFile: () => fileName.endsWith('.yml'), + }; + }), })); const { extractConfigFiles } = require('./extract_config_files'); @@ -63,3 +68,10 @@ test('ignores directories', () => { expect(config).toEqual(['path=foo.yml', 'foo.bar=/data/bar']); }); + +test('ignores directories with dots in their names', () => { + fs.existsSync = () => true; + const config = extractConfigFiles(['path=/data/foo.yml', 'foo.bar=/data/ba/r.baz'], '/es'); + + expect(config).toEqual(['path=foo.yml', 'foo.bar=/data/ba/r.baz']); +}); diff --git a/packages/kbn-es/src/utils/extract_config_files.ts b/packages/kbn-es/src/utils/extract_config_files.ts index ff07c77258d05..908005887dbc0 100644 --- a/packages/kbn-es/src/utils/extract_config_files.ts +++ b/packages/kbn-es/src/utils/extract_config_files.ts @@ -29,6 +29,7 @@ export function extractConfigFiles( if (isFile(value)) { const filename = path.basename(value); const destPath = path.resolve(dest, 'config', filename); + copyFileSync(value, destPath); options?.log.info('moved %s in config to %s', value, destPath); @@ -43,7 +44,7 @@ export function extractConfigFiles( } function isFile(dest = '') { - return path.isAbsolute(dest) && path.extname(dest).length > 0 && fs.existsSync(dest); + return fs.existsSync(dest) && fs.statSync(dest).isFile(); } function copyFileSync(src: string, dest: string) { diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 0f52afebcdc66..eb7ca100c56f8 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -17,7 +17,7 @@ pageLoadAssetSize: cloudExperiments: 59358 cloudFullStory: 18493 cloudGainsight: 18710 - cloudLinks: 17629 + cloudLinks: 55984 cloudSecurityPosture: 19109 console: 46091 contentManagement: 16254 diff --git a/packages/kbn-rrule/README.md b/packages/kbn-rrule/README.md new file mode 100644 index 0000000000000..fd59b0d019c4c --- /dev/null +++ b/packages/kbn-rrule/README.md @@ -0,0 +1,70 @@ +# @kbn/rrule + +A rewrite of [rrule.js](https://github.com/jakubroztocil/rrule) using `moment-timezone` for timezone support. Ensures that we always get the same outputs no matter what local timezone the executing system is set to. + +Differences from library on Github: + +- It is **recommended** to generate input Dates from UTC timestamps, but not required. This implementation will perform calculations on inputted Dates accurate to their corresponding Unix timestamps. +- Timezones IDs are required. They're very important for dealing with things like day-of-week changes or DST. +- `inc` argument from `between`, `before`, `after` is removed, and is computed as if it were `true` +- SECONDLY frequency is not implemented. +- This implementation may not accurately support the entire [iCalendar RRULE RFC](https://www.rfc-editor.org/rfc/rfc5545). It is known to work for common scenarios configurable in the Recurrence Scheduler UI, plus some other more complicated ones. See `rrule.test.ts` for known working configurations. + +Known not to work are mostly edge cases: + +- Manually configuring `setpos` with any frequency besides `MONTHLY` +- `wkst` doesn't seem to have an effect on anything (I was also unable to get it to affect anything in the original library though) +- Setting `byyearday` on anything besides `Frequency.YEARLY`, setting `bymonthday` on anything besides `MONTHLY`, and other similar odd situations + +## Constructor + +Create an RRule with the following options: + +```ts +new RRule({ + dtstart: Date; // Recommended to generate this from a UTC timestamp, but this impl + tzid: string; // Takes a Moment.js timezone string. Recommended to use a country and city for DST accuracy, e.g. America/Phoenix and America/Denver are both in Mountain time but Phoenix doesn't observe DST + freq?: Frequency; // Defaults to YEARLY + interval?: number; // Every x freq, e.g. 1 and YEARLY is every 1 year, 2 and WEEKLY is every 2 weeks + until?: Date; // Recur until this date + count?: number; // Number of times this rule should recur until it stops + wkst?: Weekday | number; // Start of week, defaults to Monday + // The following, if not provided, will be automatically derived from the dtstart + byweekday?: Weekday[] | string[]; // Day(s) of the week to recur, OR nth-day-of-month strings, e.g. "+2TU" second Tuesday of month, "-1FR" last Friday of the month, which will get internally converted to a byweekday/bysetpos combination + bysetpos?: number[]; // Positive or negative integer affecting nth day of the month, eg -2 combined with byweekday of FR is 2nd to last Friday of the month. Best not to set this manually and just use byweekday. + byyearday?: number[]; // Day(s) of the year that this rule should recur, e.g. 32 is Feb 1. Respects leap years. + bymonth?: number[]; // Month(s) of the year that this rule should recur + bymonthday?: number[]; // Day(s) of the momth to recur + byhour?: number[]; // Hour(s) of the day to recur + byminute?: number[]; // Minute(s) of the hour to recur + bysecond?: number[]; // Seconds(s) of the day to recur +}); +``` + +## Methods + +### `RRule.prototype.all(limit?: number)` + +returns `Date[] & { hasMore?: boolean}` + +Returns an array all dates matching the rule. By default, limited to 10000 iterations. Pass something to `limit` to change this. + +If it hits the limit, the array will come back with the property `hasMore: true`. + +### `RRule.prototype.before(dt: Date)` + +returns `Date | null` + +Returns the last recurrence before `dt`, or `null` if there is none. + +### RRule.prototype.after(dt: Date)` + +returns `Date | null` + +Returns the last recurrence after `dt`, or `null` if there is none. + +### RRule.prototype.between(start: Date, end: Date)` + +returns `Date[]` + +Returns an array of all dates between `start` and `end`. diff --git a/packages/kbn-rrule/index.ts b/packages/kbn-rrule/index.ts new file mode 100644 index 0000000000000..1ae5fab3a7699 --- /dev/null +++ b/packages/kbn-rrule/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 { RRule, Frequency, Weekday } from './rrule'; +export type { Options } from './rrule'; +export declare type WeekdayStr = 'MO' | 'TU' | 'WE' | 'TH' | 'FR' | 'SA' | 'SU'; diff --git a/packages/kbn-rrule/jest.config.js b/packages/kbn-rrule/jest.config.js new file mode 100644 index 0000000000000..fc3a26dabddd7 --- /dev/null +++ b/packages/kbn-rrule/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', + rootDir: '../..', + roots: ['/packages/kbn-rrule'], +}; diff --git a/packages/kbn-rrule/kibana.jsonc b/packages/kbn-rrule/kibana.jsonc new file mode 100644 index 0000000000000..08878a6cfb1e9 --- /dev/null +++ b/packages/kbn-rrule/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/rrule", + "owner": "@elastic/response-ops" +} diff --git a/packages/kbn-rrule/package.json b/packages/kbn-rrule/package.json new file mode 100644 index 0000000000000..79f75e59db737 --- /dev/null +++ b/packages/kbn-rrule/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/rrule", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} \ No newline at end of file diff --git a/packages/kbn-rrule/rrule.test.ts b/packages/kbn-rrule/rrule.test.ts new file mode 100644 index 0000000000000..468195315621b --- /dev/null +++ b/packages/kbn-rrule/rrule.test.ts @@ -0,0 +1,874 @@ +/* + * Copyright 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 sinon from 'sinon'; +import { RRule, Frequency, Weekday } from './rrule'; + +const DATE_2019 = '2019-01-01T00:00:00.000Z'; +const DATE_2019_DECEMBER_19 = '2019-12-19T00:00:00.000Z'; +const DATE_2019_FEB_28 = '2019-02-28T00:00:00.000Z'; +const DATE_2020 = '2020-01-01T00:00:00.000Z'; +const DATE_2020_MINUS_1_MONTH = '2019-12-01T00:00:00.000Z'; +const DATE_2020_FEB_28 = '2020-02-28T00:00:00.000Z'; +const DATE_2023 = '2023-01-01T00:00:00.000Z'; +const DATE_2023_JAN_6_11PM = '2023-01-06T23:00:00Z'; + +const INVALID_DATE = '2020-01-01-01-01T:00:00:00Z'; + +const NOW = DATE_2020; + +let fakeTimer: sinon.SinonFakeTimers; + +describe('RRule', () => { + beforeAll(() => { + fakeTimer = sinon.useFakeTimers(new Date(NOW)); + }); + + afterAll(() => fakeTimer.restore()); + + describe('frequency', () => { + it('works with yearly', () => { + const rule = new RRule({ + dtstart: new Date(DATE_2019), + freq: Frequency.YEARLY, + interval: 1, + tzid: 'UTC', + }); + + expect(rule.all(10)).toMatchInlineSnapshot(` + Array [ + 2019-01-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + 2021-01-01T00:00:00.000Z, + 2022-01-01T00:00:00.000Z, + 2023-01-01T00:00:00.000Z, + 2024-01-01T00:00:00.000Z, + 2025-01-01T00:00:00.000Z, + 2026-01-01T00:00:00.000Z, + 2027-01-01T00:00:00.000Z, + 2028-01-01T00:00:00.000Z, + ] + `); + + const rule2 = new RRule({ + dtstart: new Date(DATE_2020), + freq: Frequency.YEARLY, + interval: 3, + tzid: 'UTC', + }); + + expect(rule2.all(10)).toMatchInlineSnapshot(` + Array [ + 2020-01-01T00:00:00.000Z, + 2023-01-01T00:00:00.000Z, + 2026-01-01T00:00:00.000Z, + 2029-01-01T00:00:00.000Z, + 2032-01-01T00:00:00.000Z, + 2035-01-01T00:00:00.000Z, + 2038-01-01T00:00:00.000Z, + 2041-01-01T00:00:00.000Z, + 2044-01-01T00:00:00.000Z, + 2047-01-01T00:00:00.000Z, + ] + `); + }); + + it('works with monthly', () => { + const rule = new RRule({ + dtstart: new Date(DATE_2019), + freq: Frequency.MONTHLY, + interval: 1, + tzid: 'UTC', + }); + + expect(rule.all(15)).toMatchInlineSnapshot(` + Array [ + 2019-01-01T00:00:00.000Z, + 2019-02-01T00:00:00.000Z, + 2019-03-01T00:00:00.000Z, + 2019-04-01T00:00:00.000Z, + 2019-05-01T00:00:00.000Z, + 2019-06-01T00:00:00.000Z, + 2019-07-01T00:00:00.000Z, + 2019-08-01T00:00:00.000Z, + 2019-09-01T00:00:00.000Z, + 2019-10-01T00:00:00.000Z, + 2019-11-01T00:00:00.000Z, + 2019-12-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + 2020-02-01T00:00:00.000Z, + 2020-03-01T00:00:00.000Z, + ] + `); + + const rule2 = new RRule({ + dtstart: new Date(DATE_2019), + freq: Frequency.MONTHLY, + interval: 6, + tzid: 'UTC', + }); + + expect(rule2.all(6)).toMatchInlineSnapshot(` + Array [ + 2019-01-01T00:00:00.000Z, + 2019-07-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + 2020-07-01T00:00:00.000Z, + 2021-01-01T00:00:00.000Z, + 2021-07-01T00:00:00.000Z, + ] + `); + + const rule3 = new RRule({ + dtstart: new Date(DATE_2019), + bymonthday: [10, 20], + freq: Frequency.MONTHLY, + interval: 6, + tzid: 'UTC', + }); + + expect(rule3.all(6)).toMatchInlineSnapshot(` + Array [ + 2019-01-10T00:00:00.000Z, + 2019-01-20T00:00:00.000Z, + 2019-07-10T00:00:00.000Z, + 2019-07-20T00:00:00.000Z, + 2020-01-10T00:00:00.000Z, + 2020-01-20T00:00:00.000Z, + ] + `); + }); + + it('works with weekly', () => { + const rule = new RRule({ + dtstart: new Date(DATE_2019_DECEMBER_19), + freq: Frequency.WEEKLY, + interval: 1, + tzid: 'UTC', + }); + + expect(rule.all(14)).toMatchInlineSnapshot(` + Array [ + 2019-12-19T00:00:00.000Z, + 2019-12-26T00:00:00.000Z, + 2020-01-02T00:00:00.000Z, + 2020-01-09T00:00:00.000Z, + 2020-01-16T00:00:00.000Z, + 2020-01-23T00:00:00.000Z, + 2020-01-30T00:00:00.000Z, + 2020-02-06T00:00:00.000Z, + 2020-02-13T00:00:00.000Z, + 2020-02-20T00:00:00.000Z, + 2020-02-27T00:00:00.000Z, + 2020-03-05T00:00:00.000Z, + 2020-03-12T00:00:00.000Z, + 2020-03-19T00:00:00.000Z, + ] + `); + + const rule2 = new RRule({ + dtstart: new Date(DATE_2019_DECEMBER_19), + freq: Frequency.WEEKLY, + interval: 2, + tzid: 'UTC', + }); + + expect(rule2.all(14)).toMatchInlineSnapshot(` + Array [ + 2019-12-19T00:00:00.000Z, + 2020-01-02T00:00:00.000Z, + 2020-01-16T00:00:00.000Z, + 2020-01-30T00:00:00.000Z, + 2020-02-13T00:00:00.000Z, + 2020-02-27T00:00:00.000Z, + 2020-03-12T00:00:00.000Z, + 2020-03-26T00:00:00.000Z, + 2020-04-09T00:00:00.000Z, + 2020-04-23T00:00:00.000Z, + 2020-05-07T00:00:00.000Z, + 2020-05-21T00:00:00.000Z, + 2020-06-04T00:00:00.000Z, + 2020-06-18T00:00:00.000Z, + ] + `); + }); + + it('works with daily', () => { + const rule = new RRule({ + dtstart: new Date(DATE_2019_DECEMBER_19), + freq: Frequency.DAILY, + interval: 1, + tzid: 'UTC', + }); + + expect(rule.all(30)).toMatchInlineSnapshot(` + Array [ + 2019-12-19T00:00:00.000Z, + 2019-12-20T00:00:00.000Z, + 2019-12-21T00:00:00.000Z, + 2019-12-22T00:00:00.000Z, + 2019-12-23T00:00:00.000Z, + 2019-12-24T00:00:00.000Z, + 2019-12-25T00:00:00.000Z, + 2019-12-26T00:00:00.000Z, + 2019-12-27T00:00:00.000Z, + 2019-12-28T00:00:00.000Z, + 2019-12-29T00:00:00.000Z, + 2019-12-30T00:00:00.000Z, + 2019-12-31T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + 2020-01-02T00:00:00.000Z, + 2020-01-03T00:00:00.000Z, + 2020-01-04T00:00:00.000Z, + 2020-01-05T00:00:00.000Z, + 2020-01-06T00:00:00.000Z, + 2020-01-07T00:00:00.000Z, + 2020-01-08T00:00:00.000Z, + 2020-01-09T00:00:00.000Z, + 2020-01-10T00:00:00.000Z, + 2020-01-11T00:00:00.000Z, + 2020-01-12T00:00:00.000Z, + 2020-01-13T00:00:00.000Z, + 2020-01-14T00:00:00.000Z, + 2020-01-15T00:00:00.000Z, + 2020-01-16T00:00:00.000Z, + 2020-01-17T00:00:00.000Z, + ] + `); + + const rule2 = new RRule({ + dtstart: new Date(DATE_2019_DECEMBER_19), + freq: Frequency.DAILY, + interval: 48, + tzid: 'UTC', + }); + + expect(rule2.all(12)).toMatchInlineSnapshot(` + Array [ + 2019-12-19T00:00:00.000Z, + 2020-02-05T00:00:00.000Z, + 2020-03-24T00:00:00.000Z, + 2020-05-11T00:00:00.000Z, + 2020-06-28T00:00:00.000Z, + 2020-08-15T00:00:00.000Z, + 2020-10-02T00:00:00.000Z, + 2020-11-19T00:00:00.000Z, + 2021-01-06T00:00:00.000Z, + 2021-02-23T00:00:00.000Z, + 2021-04-12T00:00:00.000Z, + 2021-05-30T00:00:00.000Z, + ] + `); + + const rule3 = new RRule({ + dtstart: new Date(DATE_2019_FEB_28), + freq: Frequency.DAILY, + interval: 1, + tzid: 'UTC', + }); + + expect(rule3.all(6)).toMatchInlineSnapshot(` + Array [ + 2019-02-28T00:00:00.000Z, + 2019-03-01T00:00:00.000Z, + 2019-03-02T00:00:00.000Z, + 2019-03-03T00:00:00.000Z, + 2019-03-04T00:00:00.000Z, + 2019-03-05T00:00:00.000Z, + ] + `); + + const rule4 = new RRule({ + dtstart: new Date(DATE_2020_FEB_28), + freq: Frequency.DAILY, + interval: 1, + tzid: 'UTC', + }); + + expect(rule4.all(6)).toMatchInlineSnapshot(` + Array [ + 2020-02-28T00:00:00.000Z, + 2020-02-29T00:00:00.000Z, + 2020-03-01T00:00:00.000Z, + 2020-03-02T00:00:00.000Z, + 2020-03-03T00:00:00.000Z, + 2020-03-04T00:00:00.000Z, + ] + `); + }); + + it('works with hourly', () => { + const rule = new RRule({ + dtstart: new Date(DATE_2019), + freq: Frequency.HOURLY, + interval: 1, + tzid: 'UTC', + }); + + expect(rule.all(30)).toMatchInlineSnapshot(` + Array [ + 2019-01-01T00:00:00.000Z, + 2019-01-01T01:00:00.000Z, + 2019-01-01T02:00:00.000Z, + 2019-01-01T03:00:00.000Z, + 2019-01-01T04:00:00.000Z, + 2019-01-01T05:00:00.000Z, + 2019-01-01T06:00:00.000Z, + 2019-01-01T07:00:00.000Z, + 2019-01-01T08:00:00.000Z, + 2019-01-01T09:00:00.000Z, + 2019-01-01T10:00:00.000Z, + 2019-01-01T11:00:00.000Z, + 2019-01-01T12:00:00.000Z, + 2019-01-01T13:00:00.000Z, + 2019-01-01T14:00:00.000Z, + 2019-01-01T15:00:00.000Z, + 2019-01-01T16:00:00.000Z, + 2019-01-01T17:00:00.000Z, + 2019-01-01T18:00:00.000Z, + 2019-01-01T19:00:00.000Z, + 2019-01-01T20:00:00.000Z, + 2019-01-01T21:00:00.000Z, + 2019-01-01T22:00:00.000Z, + 2019-01-01T23:00:00.000Z, + 2019-01-02T00:00:00.000Z, + 2019-01-02T01:00:00.000Z, + 2019-01-02T02:00:00.000Z, + 2019-01-02T03:00:00.000Z, + 2019-01-02T04:00:00.000Z, + 2019-01-02T05:00:00.000Z, + ] + `); + + const rule2 = new RRule({ + dtstart: new Date(DATE_2019), + freq: Frequency.HOURLY, + interval: 36, + tzid: 'UTC', + }); + + expect(rule2.all(30)).toMatchInlineSnapshot(` + Array [ + 2019-01-01T00:00:00.000Z, + 2019-01-02T12:00:00.000Z, + 2019-01-04T00:00:00.000Z, + 2019-01-05T12:00:00.000Z, + 2019-01-07T00:00:00.000Z, + 2019-01-08T12:00:00.000Z, + 2019-01-10T00:00:00.000Z, + 2019-01-11T12:00:00.000Z, + 2019-01-13T00:00:00.000Z, + 2019-01-14T12:00:00.000Z, + 2019-01-16T00:00:00.000Z, + 2019-01-17T12:00:00.000Z, + 2019-01-19T00:00:00.000Z, + 2019-01-20T12:00:00.000Z, + 2019-01-22T00:00:00.000Z, + 2019-01-23T12:00:00.000Z, + 2019-01-25T00:00:00.000Z, + 2019-01-26T12:00:00.000Z, + 2019-01-28T00:00:00.000Z, + 2019-01-29T12:00:00.000Z, + 2019-01-31T00:00:00.000Z, + 2019-02-01T12:00:00.000Z, + 2019-02-03T00:00:00.000Z, + 2019-02-04T12:00:00.000Z, + 2019-02-06T00:00:00.000Z, + 2019-02-07T12:00:00.000Z, + 2019-02-09T00:00:00.000Z, + 2019-02-10T12:00:00.000Z, + 2019-02-12T00:00:00.000Z, + 2019-02-13T12:00:00.000Z, + ] + `); + }); + + it('works with minutely', () => { + const rule = new RRule({ + dtstart: new Date(DATE_2019), + freq: Frequency.MINUTELY, + interval: 15, + tzid: 'UTC', + }); + + expect(rule.all(10)).toMatchInlineSnapshot(` + Array [ + 2019-01-01T00:00:00.000Z, + 2019-01-01T00:15:00.000Z, + 2019-01-01T00:30:00.000Z, + 2019-01-01T00:45:00.000Z, + 2019-01-01T01:00:00.000Z, + 2019-01-01T01:15:00.000Z, + 2019-01-01T01:30:00.000Z, + 2019-01-01T01:45:00.000Z, + 2019-01-01T02:00:00.000Z, + 2019-01-01T02:15:00.000Z, + ] + `); + + const rule2 = new RRule({ + dtstart: new Date(DATE_2019), + freq: Frequency.MINUTELY, + interval: 36, + tzid: 'UTC', + }); + + expect(rule2.all(30)).toMatchInlineSnapshot(` + Array [ + 2019-01-01T00:00:00.000Z, + 2019-01-01T00:36:00.000Z, + 2019-01-01T01:12:00.000Z, + 2019-01-01T01:48:00.000Z, + 2019-01-01T02:24:00.000Z, + 2019-01-01T03:00:00.000Z, + 2019-01-01T03:36:00.000Z, + 2019-01-01T04:12:00.000Z, + 2019-01-01T04:48:00.000Z, + 2019-01-01T05:24:00.000Z, + 2019-01-01T06:00:00.000Z, + 2019-01-01T06:36:00.000Z, + 2019-01-01T07:12:00.000Z, + 2019-01-01T07:48:00.000Z, + 2019-01-01T08:24:00.000Z, + 2019-01-01T09:00:00.000Z, + 2019-01-01T09:36:00.000Z, + 2019-01-01T10:12:00.000Z, + 2019-01-01T10:48:00.000Z, + 2019-01-01T11:24:00.000Z, + 2019-01-01T12:00:00.000Z, + 2019-01-01T12:36:00.000Z, + 2019-01-01T13:12:00.000Z, + 2019-01-01T13:48:00.000Z, + 2019-01-01T14:24:00.000Z, + 2019-01-01T15:00:00.000Z, + 2019-01-01T15:36:00.000Z, + 2019-01-01T16:12:00.000Z, + 2019-01-01T16:48:00.000Z, + 2019-01-01T17:24:00.000Z, + ] + `); + }); + }); + + it('works with until', () => { + const rule = new RRule({ + dtstart: new Date(DATE_2019), + freq: Frequency.MONTHLY, + interval: 1, + tzid: 'UTC', + until: new Date(DATE_2020_MINUS_1_MONTH), + }); + expect(rule.all().length).toBe(12); + }); + + it('works with count', () => { + const rule = new RRule({ + dtstart: new Date(DATE_2019), + freq: Frequency.MONTHLY, + interval: 1, + tzid: 'UTC', + count: 20, + }); + expect(rule.all()).toMatchInlineSnapshot(` + Array [ + 2019-01-01T00:00:00.000Z, + 2019-02-01T00:00:00.000Z, + 2019-03-01T00:00:00.000Z, + 2019-04-01T00:00:00.000Z, + 2019-05-01T00:00:00.000Z, + 2019-06-01T00:00:00.000Z, + 2019-07-01T00:00:00.000Z, + 2019-08-01T00:00:00.000Z, + 2019-09-01T00:00:00.000Z, + 2019-10-01T00:00:00.000Z, + 2019-11-01T00:00:00.000Z, + 2019-12-01T00:00:00.000Z, + 2020-01-01T00:00:00.000Z, + 2020-02-01T00:00:00.000Z, + 2020-03-01T00:00:00.000Z, + 2020-04-01T00:00:00.000Z, + 2020-05-01T00:00:00.000Z, + 2020-06-01T00:00:00.000Z, + 2020-07-01T00:00:00.000Z, + 2020-08-01T00:00:00.000Z, + ] + `); + }); + + describe('byweekday', () => { + it('works with weekly frequency', () => { + const rule = new RRule({ + dtstart: new Date(DATE_2019_DECEMBER_19), + freq: Frequency.WEEKLY, + interval: 1, + tzid: 'UTC', + byweekday: [Weekday.TH], + }); + expect(rule.all(14)).toMatchInlineSnapshot(` + Array [ + 2019-12-19T00:00:00.000Z, + 2019-12-26T00:00:00.000Z, + 2020-01-02T00:00:00.000Z, + 2020-01-09T00:00:00.000Z, + 2020-01-16T00:00:00.000Z, + 2020-01-23T00:00:00.000Z, + 2020-01-30T00:00:00.000Z, + 2020-02-06T00:00:00.000Z, + 2020-02-13T00:00:00.000Z, + 2020-02-20T00:00:00.000Z, + 2020-02-27T00:00:00.000Z, + 2020-03-05T00:00:00.000Z, + 2020-03-12T00:00:00.000Z, + 2020-03-19T00:00:00.000Z, + ] + `); + + const rule2 = new RRule({ + dtstart: new Date(DATE_2019), + freq: Frequency.WEEKLY, + interval: 1, + tzid: 'UTC', + byweekday: [Weekday.SA, Weekday.SU, Weekday.MO], + }); + + expect(rule2.all(9)).toMatchInlineSnapshot(` + Array [ + 2019-01-05T00:00:00.000Z, + 2019-01-06T00:00:00.000Z, + 2019-01-07T00:00:00.000Z, + 2019-01-12T00:00:00.000Z, + 2019-01-13T00:00:00.000Z, + 2019-01-14T00:00:00.000Z, + 2019-01-19T00:00:00.000Z, + 2019-01-20T00:00:00.000Z, + 2019-01-21T00:00:00.000Z, + ] + `); + }); + + it('works with daily frequency by behaving like weekly frequency', () => { + const rule = new RRule({ + dtstart: new Date(DATE_2019_DECEMBER_19), + freq: Frequency.DAILY, + interval: 1, + tzid: 'UTC', + byweekday: [Weekday.TH], + }); + expect(rule.all(14)).toMatchInlineSnapshot(` + Array [ + 2019-12-19T00:00:00.000Z, + 2019-12-26T00:00:00.000Z, + 2020-01-02T00:00:00.000Z, + 2020-01-09T00:00:00.000Z, + 2020-01-16T00:00:00.000Z, + 2020-01-23T00:00:00.000Z, + 2020-01-30T00:00:00.000Z, + 2020-02-06T00:00:00.000Z, + 2020-02-13T00:00:00.000Z, + 2020-02-20T00:00:00.000Z, + 2020-02-27T00:00:00.000Z, + 2020-03-05T00:00:00.000Z, + 2020-03-12T00:00:00.000Z, + 2020-03-19T00:00:00.000Z, + ] + `); + + const rule2 = new RRule({ + dtstart: new Date(DATE_2019), + freq: Frequency.WEEKLY, + interval: 1, + tzid: 'UTC', + byweekday: [Weekday.SA, Weekday.SU, Weekday.MO], + }); + + expect(rule2.all(9)).toMatchInlineSnapshot(` + Array [ + 2019-01-05T00:00:00.000Z, + 2019-01-06T00:00:00.000Z, + 2019-01-07T00:00:00.000Z, + 2019-01-12T00:00:00.000Z, + 2019-01-13T00:00:00.000Z, + 2019-01-14T00:00:00.000Z, + 2019-01-19T00:00:00.000Z, + 2019-01-20T00:00:00.000Z, + 2019-01-21T00:00:00.000Z, + ] + `); + }); + + it('works with monthly frequency with non-setpos syntax by behaving like weekly frequency', () => { + const rule = new RRule({ + dtstart: new Date(DATE_2019_DECEMBER_19), + freq: Frequency.DAILY, + interval: 1, + tzid: 'UTC', + byweekday: [Weekday.TH], + }); + expect(rule.all(14)).toMatchInlineSnapshot(` + Array [ + 2019-12-19T00:00:00.000Z, + 2019-12-26T00:00:00.000Z, + 2020-01-02T00:00:00.000Z, + 2020-01-09T00:00:00.000Z, + 2020-01-16T00:00:00.000Z, + 2020-01-23T00:00:00.000Z, + 2020-01-30T00:00:00.000Z, + 2020-02-06T00:00:00.000Z, + 2020-02-13T00:00:00.000Z, + 2020-02-20T00:00:00.000Z, + 2020-02-27T00:00:00.000Z, + 2020-03-05T00:00:00.000Z, + 2020-03-12T00:00:00.000Z, + 2020-03-19T00:00:00.000Z, + ] + `); + + const rule2 = new RRule({ + dtstart: new Date(DATE_2019), + freq: Frequency.WEEKLY, + interval: 1, + tzid: 'UTC', + byweekday: [Weekday.SA, Weekday.SU, Weekday.MO], + }); + + expect(rule2.all(9)).toMatchInlineSnapshot(` + Array [ + 2019-01-05T00:00:00.000Z, + 2019-01-06T00:00:00.000Z, + 2019-01-07T00:00:00.000Z, + 2019-01-12T00:00:00.000Z, + 2019-01-13T00:00:00.000Z, + 2019-01-14T00:00:00.000Z, + 2019-01-19T00:00:00.000Z, + 2019-01-20T00:00:00.000Z, + 2019-01-21T00:00:00.000Z, + ] + `); + }); + + it('works with monthly frequency using setpos syntax', () => { + const rule = new RRule({ + dtstart: new Date(DATE_2023), + freq: Frequency.MONTHLY, + interval: 1, + tzid: 'UTC', + byweekday: ['+1TU', '+2TU', '-1FR', '-2FR'], + }); + const result = rule.all(12); + + expect(result).toMatchInlineSnapshot(` + Array [ + 2023-01-03T00:00:00.000Z, + 2023-01-10T00:00:00.000Z, + 2023-01-20T00:00:00.000Z, + 2023-01-27T00:00:00.000Z, + 2023-02-07T00:00:00.000Z, + 2023-02-14T00:00:00.000Z, + 2023-02-17T00:00:00.000Z, + 2023-02-24T00:00:00.000Z, + 2023-03-07T00:00:00.000Z, + 2023-03-14T00:00:00.000Z, + 2023-03-24T00:00:00.000Z, + 2023-03-31T00:00:00.000Z, + ] + `); + }); + + it('works with timezones', () => { + const rule = new RRule({ + dtstart: new Date(DATE_2023_JAN_6_11PM), + freq: Frequency.WEEKLY, + interval: 1, + tzid: 'Europe/Madrid', + byweekday: [Weekday.SA], + }); + expect(rule.all(12)).toMatchInlineSnapshot(` + Array [ + 2023-01-06T23:00:00.000Z, + 2023-01-13T23:00:00.000Z, + 2023-01-20T23:00:00.000Z, + 2023-01-27T23:00:00.000Z, + 2023-02-03T23:00:00.000Z, + 2023-02-10T23:00:00.000Z, + 2023-02-17T23:00:00.000Z, + 2023-02-24T23:00:00.000Z, + 2023-03-03T23:00:00.000Z, + 2023-03-10T23:00:00.000Z, + 2023-03-17T23:00:00.000Z, + 2023-03-24T23:00:00.000Z, + ] + `); + + const rule2 = new RRule({ + dtstart: new Date(DATE_2023_JAN_6_11PM), + freq: Frequency.WEEKLY, + interval: 1, + tzid: 'UTC', + byweekday: [Weekday.SA], + }); + + expect(rule2.all(12)).toMatchInlineSnapshot(` + Array [ + 2023-01-07T23:00:00.000Z, + 2023-01-14T23:00:00.000Z, + 2023-01-21T23:00:00.000Z, + 2023-01-28T23:00:00.000Z, + 2023-02-04T23:00:00.000Z, + 2023-02-11T23:00:00.000Z, + 2023-02-18T23:00:00.000Z, + 2023-02-25T23:00:00.000Z, + 2023-03-04T23:00:00.000Z, + 2023-03-11T23:00:00.000Z, + 2023-03-18T23:00:00.000Z, + 2023-03-25T23:00:00.000Z, + ] + `); + }); + }); + + describe('byhour, byminute, bysecond', () => { + it('works with daily frequency', () => { + const rule = new RRule({ + dtstart: new Date(DATE_2019_DECEMBER_19), + freq: Frequency.DAILY, + interval: 1, + tzid: 'UTC', + byhour: [14], + byminute: [30], + bysecond: [0, 15], + }); + expect(rule.all(14)).toMatchInlineSnapshot(` + Array [ + 2019-12-19T14:30:00.000Z, + 2019-12-19T14:30:15.000Z, + 2019-12-20T14:30:00.000Z, + 2019-12-20T14:30:15.000Z, + 2019-12-21T14:30:00.000Z, + 2019-12-21T14:30:15.000Z, + 2019-12-22T14:30:00.000Z, + 2019-12-22T14:30:15.000Z, + 2019-12-23T14:30:00.000Z, + 2019-12-23T14:30:15.000Z, + 2019-12-24T14:30:00.000Z, + 2019-12-24T14:30:15.000Z, + 2019-12-25T14:30:00.000Z, + 2019-12-25T14:30:15.000Z, + ] + `); + }); + it('works with hourly frequency', () => { + const rule = new RRule({ + dtstart: new Date(DATE_2019_DECEMBER_19), + freq: Frequency.HOURLY, + interval: 1, + tzid: 'UTC', + byminute: [15, 30], + bysecond: [30, 0], + }); + expect(rule.all(14)).toMatchInlineSnapshot(` + Array [ + 2019-12-19T00:15:30.000Z, + 2019-12-19T00:15:00.000Z, + 2019-12-19T00:30:30.000Z, + 2019-12-19T00:30:00.000Z, + 2019-12-19T01:15:30.000Z, + 2019-12-19T01:15:00.000Z, + 2019-12-19T01:30:30.000Z, + 2019-12-19T01:30:00.000Z, + 2019-12-19T02:15:30.000Z, + 2019-12-19T02:15:00.000Z, + 2019-12-19T02:30:30.000Z, + 2019-12-19T02:30:00.000Z, + 2019-12-19T03:15:30.000Z, + 2019-12-19T03:15:00.000Z, + ] + `); + }); + it('works with minutely frequency', () => { + const rule = new RRule({ + dtstart: new Date(DATE_2019_DECEMBER_19), + freq: Frequency.HOURLY, + interval: 1, + tzid: 'UTC', + bysecond: [10, 30, 58], + }); + expect(rule.all(14)).toMatchInlineSnapshot(` + Array [ + 2019-12-19T00:00:10.000Z, + 2019-12-19T00:00:30.000Z, + 2019-12-19T00:00:58.000Z, + 2019-12-19T00:01:10.000Z, + 2019-12-19T00:01:30.000Z, + 2019-12-19T00:01:58.000Z, + 2019-12-19T00:02:10.000Z, + 2019-12-19T00:02:30.000Z, + 2019-12-19T00:02:58.000Z, + 2019-12-19T00:03:10.000Z, + 2019-12-19T00:03:30.000Z, + 2019-12-19T00:03:58.000Z, + 2019-12-19T00:04:10.000Z, + 2019-12-19T00:04:30.000Z, + ] + `); + }); + }); + + describe('byyearday', () => { + it('respects leap years', () => { + const rule3 = new RRule({ + dtstart: new Date(DATE_2020), + freq: Frequency.YEARLY, + byyearday: [92], + interval: 1, + tzid: 'UTC', + }); + + expect(rule3.all(10)).toMatchInlineSnapshot(` + Array [ + 2020-04-01T00:00:00.000Z, + 2021-04-02T00:00:00.000Z, + 2022-04-02T00:00:00.000Z, + 2023-04-02T00:00:00.000Z, + 2024-04-01T00:00:00.000Z, + 2025-04-02T00:00:00.000Z, + 2026-04-02T00:00:00.000Z, + 2027-04-02T00:00:00.000Z, + 2028-04-01T00:00:00.000Z, + 2029-04-02T00:00:00.000Z, + ] + `); + }); + }); + + describe('error handling', () => { + it('throws an error on an invalid dtstart', () => { + const testFn = () => + new RRule({ + dtstart: new Date(INVALID_DATE), + freq: Frequency.HOURLY, + interval: 1, + tzid: 'UTC', + }); + expect(testFn).toThrowErrorMatchingInlineSnapshot( + `"Cannot create RRule: dtstart is an invalid date"` + ); + }); + it('throws an error on an invalid until', () => { + const testFn = () => + new RRule({ + dtstart: new Date(DATE_2020), + until: new Date(INVALID_DATE), + freq: Frequency.HOURLY, + interval: 1, + tzid: 'UTC', + }); + expect(testFn).toThrowErrorMatchingInlineSnapshot( + `"Cannot create RRule: until is an invalid date"` + ); + }); + }); +}); diff --git a/packages/kbn-rrule/rrule.ts b/packages/kbn-rrule/rrule.ts new file mode 100644 index 0000000000000..347a3f986bf12 --- /dev/null +++ b/packages/kbn-rrule/rrule.ts @@ -0,0 +1,432 @@ +/* + * Copyright 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 moment, { Moment } from 'moment-timezone'; + +export enum Frequency { + YEARLY = 0, + MONTHLY = 1, + WEEKLY = 2, + DAILY = 3, + HOURLY = 4, + MINUTELY = 5, +} + +export enum Weekday { + MO = 1, + TU = 2, + WE = 3, + TH = 4, + FR = 5, + SA = 6, + SU = 7, +} + +export type WeekdayStr = 'MO' | 'TU' | 'WE' | 'TH' | 'FR' | 'SA' | 'SU'; +interface IterOptions { + refDT: Moment; + wkst?: Weekday | number | null; + byyearday?: number[] | null; + bymonth?: number[] | null; + bysetpos?: number[] | null; + bymonthday?: number[] | null; + byweekday?: Weekday[] | null; + byhour?: number[] | null; + byminute?: number[] | null; + bysecond?: number[] | null; +} + +type Options = Omit & { + dtstart: Date; + freq?: Frequency; + interval?: number; + until?: Date | null; + count?: number; + tzid: string; +}; + +type ConstructorOptions = Omit & { + byweekday?: Array | null; + wkst?: Weekday | WeekdayStr | number | null; +}; + +export type { ConstructorOptions as Options }; + +const ISO_WEEKDAYS = [ + Weekday.MO, + Weekday.TU, + Weekday.WE, + Weekday.TH, + Weekday.FR, + Weekday.SA, + Weekday.SU, +]; + +type AllResult = Date[] & { + hasMore?: boolean; +}; + +const ALL_LIMIT = 10000; + +export class RRule { + private options: Options; + constructor(options: ConstructorOptions) { + this.options = options as Options; + if (isNaN(options.dtstart.getTime())) { + throw new Error('Cannot create RRule: dtstart is an invalid date'); + } + if (options.until && isNaN(options.until.getTime())) { + throw new Error('Cannot create RRule: until is an invalid date'); + } + if (typeof options.wkst === 'string') { + this.options.wkst = Weekday[options.wkst]; + } + const weekdayParseResult = parseByWeekdayPos(options.byweekday); + if (weekdayParseResult) { + this.options.byweekday = weekdayParseResult[0]; + this.options.bysetpos = weekdayParseResult[1]; + } + } + + private *dateset(start?: Date, end?: Date): Generator { + const isAfterDtStart = (current: Date) => current.getTime() >= this.options.dtstart.getTime(); + const isInBounds = (current: Date) => { + const afterStart = !start || current.getTime() >= start.getTime(); + const beforeEnd = !end || current.getTime() <= end.getTime(); + + return afterStart && beforeEnd; + }; + + const { dtstart, tzid, count, until } = this.options; + let isFirstIteration = true; + let yieldedRecurrenceCount = 0; + let current: Date = moment(dtstart ?? new Date()) + .tz(tzid) + .toDate(); + + const nextRecurrences: Moment[] = []; + + while ( + (!count && !until) || + (count && yieldedRecurrenceCount < count) || + (until && current.getTime() < new Date(until).getTime()) + ) { + const next = nextRecurrences.shift()?.toDate(); + if (next) { + current = next; + if (!isAfterDtStart(current)) continue; + yieldedRecurrenceCount++; + if (isInBounds(current)) { + yield current; + } else if (start && current.getTime() > start.getTime()) { + return null; + } + } else { + getNextRecurrences({ + refDT: moment(current).tz(tzid), + ...this.options, + interval: isFirstIteration ? 0 : this.options.interval, + wkst: this.options.wkst ? (this.options.wkst as Weekday) : Weekday.MO, + }).forEach((r) => nextRecurrences.push(r)); + isFirstIteration = false; + if (nextRecurrences.length === 0) { + return null; + } + } + } + + return null; + } + + between(start: Date, end: Date) { + const dates = this.dateset(start, end); + return [...dates]; + } + + before(dt: Date) { + const dates = [...this.dateset(this.options.dtstart, dt)]; + return dates[dates.length - 1]; + } + + after(dt: Date) { + const dates = this.dateset(dt); + return dates.next().value; + } + + all(limit: number = ALL_LIMIT): AllResult { + const dateGenerator = this.dateset(); + const dates: AllResult = []; + let next = dateGenerator.next(); + for (let i = 0; i < limit; i++) { + if (!next.done) dates.push(next.value); + else break; + next = dateGenerator.next(); + } + if (next.done) return dates; + else { + dates.hasMore = true; + return dates; + } + } +} + +const parseByWeekdayPos = function (byweekday: ConstructorOptions['byweekday']) { + if (byweekday?.some((d) => typeof d === 'string')) { + const pos: number[] = []; + const newByweekday = byweekday.map((d) => { + if (typeof d !== 'string') return d; + if (Object.keys(Weekday).includes(d)) return Weekday[d as WeekdayStr]; + const [sign, number, ...rest] = d.split(''); + if (sign === '-') pos.push(-Number(number)); + else pos.push(Number(number)); + return Weekday[rest.join('') as WeekdayStr]; + }); + return [newByweekday, pos]; + } else return null; +}; + +export const getNextRecurrences = function ({ + refDT, + wkst = Weekday.MO, + byyearday, + bymonth, + bymonthday, + byweekday, + byhour, + byminute, + bysecond, + bysetpos, + freq = Frequency.YEARLY, + interval = 1, +}: IterOptions & { + freq?: Frequency; + interval?: number; +}) { + const opts = { + wkst, + byyearday, + bymonth, + bymonthday, + byweekday, + byhour, + byminute, + bysecond, + bysetpos, + }; + + // If the frequency is DAILY but there's a byweekday, or if the frequency is MONTHLY with a byweekday with no + // corresponding bysetpos, use the WEEKLY code path to determine recurrences + const derivedFreq = + byweekday && (freq === Frequency.DAILY || (freq === Frequency.MONTHLY && !bysetpos?.length)) + ? Frequency.WEEKLY + : freq; + + switch (derivedFreq) { + case Frequency.YEARLY: { + const nextRef = moment(refDT).add(interval, 'y'); + return getYearOfRecurrences({ + refDT: nextRef, + ...opts, + }); + } + case Frequency.MONTHLY: { + const nextRef = moment(refDT).add(interval, 'M'); + return getMonthOfRecurrences({ + refDT: nextRef, + ...opts, + }); + } + case Frequency.WEEKLY: { + const nextRef = moment(refDT).add(interval, 'w'); + return getWeekOfRecurrences({ + refDT: nextRef, + ...opts, + }); + } + case Frequency.DAILY: { + const nextRef = moment(refDT).add(interval, 'd'); + return getDayOfRecurrences({ + refDT: nextRef, + ...opts, + }); + } + case Frequency.HOURLY: { + const nextRef = moment(refDT).add(interval, 'h'); + return getHourOfRecurrences({ + refDT: nextRef, + ...opts, + }); + } + case Frequency.MINUTELY: { + const nextRef = moment(refDT).add(interval, 'm'); + return getMinuteOfRecurrences({ + refDT: nextRef, + ...opts, + }); + } + } +}; + +const sortByweekday = function ({ + wkst, + byweekday, +}: { + wkst?: Weekday | null; + byweekday: Weekday[]; +}) { + const weekStart = wkst ?? Weekday.MO; + const weekdays = ISO_WEEKDAYS.slice(weekStart - 1).concat(ISO_WEEKDAYS.slice(0, weekStart - 1)); + return [...byweekday].sort((a, b) => weekdays.indexOf(a) - weekdays.indexOf(b)); +}; + +const getYearOfRecurrences = function ({ + refDT, + wkst, + byyearday, + bymonth, + bymonthday, + byweekday, + byhour, + byminute, + bysecond, + bysetpos, +}: IterOptions) { + const derivedByweekday = byweekday ?? ISO_WEEKDAYS; + + if (bymonth) { + return bymonth.flatMap((month) => { + const currentMonth = moment(refDT).month(month - 1); + return getMonthOfRecurrences({ + refDT: currentMonth, + wkst, + bymonthday, + byweekday, + byhour, + byminute, + bysecond, + bysetpos, + }); + }); + } + + const derivedByyearday = byyearday ?? [refDT.dayOfYear()]; + + return derivedByyearday.flatMap((dayOfYear) => { + const currentDate = moment(refDT).dayOfYear(dayOfYear); + if (!derivedByweekday.includes(currentDate.isoWeekday())) return []; + return getDayOfRecurrences({ refDT: currentDate, byhour, byminute, bysecond }); + }); +}; + +const getMonthOfRecurrences = function ({ + refDT, + wkst, + bymonthday, + bymonth, + byweekday, + byhour, + byminute, + bysecond, + bysetpos, +}: IterOptions) { + const derivedByweekday = byweekday ?? ISO_WEEKDAYS; + const currentMonth = refDT.month(); + if (bymonth && !bymonth.includes(currentMonth)) return []; + + let derivedBymonthday = bymonthday ?? [refDT.date()]; + if (bysetpos) { + const firstOfMonth = moment(refDT).month(currentMonth).date(1); + const dowLookup: Record = { + 1: [], + 2: [], + 3: [], + 4: [], + 5: [], + 6: [], + 7: [], + }; + const trackedDate = firstOfMonth; + while (trackedDate.month() === currentMonth) { + const currentDow = trackedDate.isoWeekday() as Weekday; + dowLookup[currentDow].push(trackedDate.date()); + trackedDate.add(1, 'd'); + } + const sortedByweekday = sortByweekday({ wkst, byweekday: derivedByweekday }); + const bymonthdayFromPos = bysetpos.map((pos, i) => { + const correspondingWeekday = sortedByweekday[i]; + const lookup = dowLookup[correspondingWeekday]; + if (pos > 0) return [lookup[pos - 1], pos]; + return [lookup.slice(pos)[0], pos]; + }); + + const posPositions = [ + // Start with positive numbers in ascending order + ...bymonthdayFromPos + .filter(([, p]) => p > 0) + .sort(([, a], [, b]) => a - b) + .map(([date]) => date), + ]; + const negPositions = [ + // then negative numbers in descending order] + ...bymonthdayFromPos + .filter(([, p]) => p < 0) + .sort(([, a], [, b]) => a - b) + .map(([date]) => date), + ]; + derivedBymonthday = [...posPositions, ...negPositions]; + } + + return derivedBymonthday.flatMap((date) => { + const currentDate = moment(refDT).date(date); + if (!derivedByweekday.includes(currentDate.isoWeekday())) return []; + return getDayOfRecurrences({ refDT: currentDate, byhour, byminute, bysecond }); + }); +}; + +const getWeekOfRecurrences = function ({ + refDT, + wkst = Weekday.MO, + byweekday, + byhour, + byminute, + bysecond, +}: IterOptions) { + const derivedByweekday = byweekday ? sortByweekday({ wkst, byweekday }) : [refDT.isoWeekday()]; + + return derivedByweekday.flatMap((day) => { + const currentDay = moment(refDT).isoWeekday(day); + return getDayOfRecurrences({ refDT: currentDay, byhour, byminute, bysecond }); + }); +}; + +const getDayOfRecurrences = function ({ refDT, byhour, byminute, bysecond }: IterOptions) { + const derivedByhour = + byhour ?? (byminute || bysecond ? Array.from(Array(24), (_, i) => i) : [refDT.hour()]); + + return derivedByhour.flatMap((h) => { + const currentHour = moment(refDT).hour(h); + return getHourOfRecurrences({ refDT: currentHour, byminute, bysecond }); + }); +}; + +const getHourOfRecurrences = function ({ refDT, byminute, bysecond }: IterOptions) { + const derivedByminute = + byminute ?? (bysecond ? Array.from(Array(60), (_, i) => i) : [refDT.minute()]); + + return derivedByminute.flatMap((m) => { + const currentMinute = moment(refDT).minute(m); + return getMinuteOfRecurrences({ refDT: currentMinute, bysecond }); + }); +}; + +const getMinuteOfRecurrences = function ({ refDT, bysecond }: IterOptions) { + const derivedBysecond = bysecond ?? [refDT.second()]; + + return derivedBysecond.map((s) => { + return moment(refDT).second(s); + }); +}; diff --git a/packages/kbn-rrule/tsconfig.json b/packages/kbn-rrule/tsconfig.json new file mode 100644 index 0000000000000..87f865132f4b4 --- /dev/null +++ b/packages/kbn-rrule/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [] +} diff --git a/packages/kbn-cell-actions/src/utils.ts b/src/core/server/integration_tests/http/router.test.mocks.ts similarity index 60% rename from packages/kbn-cell-actions/src/utils.ts rename to src/core/server/integration_tests/http/router.test.mocks.ts index ef0a7c8dfb004..fd2862acd437c 100644 --- a/packages/kbn-cell-actions/src/utils.ts +++ b/src/core/server/integration_tests/http/router.test.mocks.ts @@ -6,8 +6,12 @@ * Side Public License, v 1. */ -import { KBN_FIELD_TYPES } from '@kbn/field-types'; -import { SUPPORTED_KBN_TYPES } from './constants'; +export const captureErrorMock = jest.fn(); -export const isTypeSupportedByCellActions = (kbnFieldType: KBN_FIELD_TYPES) => - SUPPORTED_KBN_TYPES.includes(kbnFieldType); +jest.doMock('elastic-apm-node', () => { + const real = jest.requireActual('elastic-apm-node'); + return { + ...real, + captureError: captureErrorMock, + }; +}); diff --git a/src/core/server/integration_tests/http/router.test.ts b/src/core/server/integration_tests/http/router.test.ts index c9dca25eea044..743624f2f46b3 100644 --- a/src/core/server/integration_tests/http/router.test.ts +++ b/src/core/server/integration_tests/http/router.test.ts @@ -6,6 +6,8 @@ * Side Public License, v 1. */ +import { captureErrorMock } from './router.test.mocks'; + import { Stream } from 'stream'; import Boom from '@hapi/boom'; import supertest from 'supertest'; @@ -35,6 +37,7 @@ beforeEach(async () => { }); afterEach(async () => { + captureErrorMock.mockReset(); await server.stop(); }); @@ -581,6 +584,22 @@ describe('Handler', () => { `); }); + it('captures the error if handler throws', async () => { + const { server: innerServer, createRouter } = await server.setup(setupDeps); + const router = createRouter('/'); + + const error = new Error(`some error`); + router.get({ path: '/', validate: false }, (context, req, res) => { + throw error; + }); + await server.start(); + + await supertest(innerServer.listener).get('/').expect(500); + + expect(captureErrorMock).toHaveBeenCalledTimes(1); + expect(captureErrorMock).toHaveBeenCalledWith(error); + }); + it('returns 500 Server error if handler throws Boom error', async () => { const { server: innerServer, createRouter } = await server.setup(setupDeps); const router = createRouter('/'); @@ -602,6 +621,7 @@ describe('Handler', () => { ], ] `); + expect(captureErrorMock).toHaveBeenCalledTimes(1); }); it('returns 500 Server error if handler returns unexpected result', async () => { diff --git a/src/core/server/integration_tests/http/versioned_router.test.mocks.ts b/src/core/server/integration_tests/http/versioned_router.test.mocks.ts new file mode 100644 index 0000000000000..fd2862acd437c --- /dev/null +++ b/src/core/server/integration_tests/http/versioned_router.test.mocks.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 const captureErrorMock = jest.fn(); + +jest.doMock('elastic-apm-node', () => { + const real = jest.requireActual('elastic-apm-node'); + return { + ...real, + captureError: captureErrorMock, + }; +}); diff --git a/src/core/server/integration_tests/http/versioned_router.test.ts b/src/core/server/integration_tests/http/versioned_router.test.ts index 0fbd64acad9ce..adde119815d78 100644 --- a/src/core/server/integration_tests/http/versioned_router.test.ts +++ b/src/core/server/integration_tests/http/versioned_router.test.ts @@ -6,6 +6,8 @@ * Side Public License, v 1. */ +import { captureErrorMock } from './versioned_router.test.mocks'; + import Supertest from 'supertest'; import { createTestEnv, getEnvOptions } from '@kbn/config-mocks'; import { schema } from '@kbn/config-schema'; @@ -59,6 +61,7 @@ describe('Routing versioned requests', () => { }); afterEach(async () => { + captureErrorMock.mockReset(); await server.stop(); }); @@ -167,6 +170,7 @@ describe('Routing versioned requests', () => { message: expect.stringMatching(/expected value of type/), }) ); + expect(captureErrorMock).not.toHaveBeenCalled(); }); it('returns the version in response headers', async () => { @@ -210,6 +214,7 @@ describe('Routing versioned requests', () => { message: expect.stringMatching(/Failed output validation/), }) ); + expect(captureErrorMock).not.toHaveBeenCalled(); }); it('does not run response validation in prod', async () => { @@ -295,6 +300,7 @@ describe('Routing versioned requests', () => { ).resolves.toEqual( expect.objectContaining({ message: expect.stringMatching(/No handlers registered/) }) ); + expect(captureErrorMock).not.toHaveBeenCalled(); }); it('resolves the newest handler on serverless', async () => { @@ -340,4 +346,21 @@ describe('Routing versioned requests', () => { .then(({ body }) => body.v) ).resolves.toEqual('oldest'); }); + + it('captures the error if handler throws', async () => { + const error = new Error(`some error`); + + router.versioned + .get({ path: '/my-path', access: 'internal' }) + .addVersion({ validate: false, version: '1' }, async (ctx, req, res) => { + throw error; + }); + + await server.start(); + + await supertest.get('/my-path').set('Elastic-Api-Version', '1').expect(500); + + expect(captureErrorMock).toHaveBeenCalledTimes(1); + expect(captureErrorMock).toHaveBeenCalledWith(error); + }); }); diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/dot_kibana_split.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/dot_kibana_split.test.ts index 06abb539c3ef7..30888521d651e 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/dot_kibana_split.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/dot_kibana_split.test.ts @@ -326,6 +326,8 @@ describe('split .kibana index into multiple system indices', () => { // .kibana_task_manager migrator is NOT involved in relocation, must not sync with other migrators '[.kibana_task_manager] READY_TO_REINDEX_SYNC', '[.kibana_task_manager] DONE_REINDEXING_SYNC', + // .kibana_task_manager migrator performed a REINDEX migration, it must update ALL types + '[.kibana_task_manager] Kibana is performing a compatible update and it will update the following SO types so that ES can pickup the updated mappings', ]); // new indices migrators did not exist, so they all have to reindex (create temp index + sync) @@ -390,6 +392,9 @@ describe('split .kibana index into multiple system indices', () => { // should NOT retransform anything (we reindexed, thus we transformed already) ['.kibana', '.kibana_task_manager', '.kibana_so_ui', '.kibana_so_search'].forEach((index) => { expect(logs).not.toContainLogEntry(`[${index}] OUTDATED_DOCUMENTS_TRANSFORM`); + expect(logs).not.toContainLogEntry( + `[${index}] Kibana is performing a compatible update and it will update the following SO types so that ES can pickup the updated mappings` + ); }); }); @@ -407,9 +412,8 @@ describe('split .kibana index into multiple system indices', () => { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/157510 - // This test takes too long. Can be manually executed to verify the correct behavior. - describe.skip('when multiple Kibana migrators run in parallel', () => { + describe('when multiple Kibana migrators run in parallel', () => { + jest.setTimeout(1200000); it('correctly migrates 7.7.2_xpack_100k_obj.zip archive', async () => { esServer = await startElasticsearch({ dataArchive: Path.join(__dirname, '..', 'archives', '7.7.2_xpack_100k_obj.zip'), @@ -486,7 +490,7 @@ describe('split .kibana index into multiple system indices', () => { task: 5, }, }); - }, 1200000); + }); afterEach(async () => { await esServer?.stop(); diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/pickup_updated_types_only.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/pickup_updated_types_only.test.ts new file mode 100644 index 0000000000000..5b2ab045ca2f0 --- /dev/null +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/pickup_updated_types_only.test.ts @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import Path from 'path'; +import type { TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server'; +import { + clearLog, + createBaseline, + currentVersion, + defaultKibanaIndex, + defaultLogFilePath, + getCompatibleMappingsMigrator, + getIncompatibleMappingsMigrator, + startElasticsearch, +} from '../kibana_migrator_test_kit'; +import '../jest_matchers'; +import { delay, parseLogFile } from '../test_utils'; +import { IndexMappingMeta } from '@kbn/core-saved-objects-base-server-internal'; + +export const logFilePath = Path.join(__dirname, 'pickup_updated_types_only.test.log'); + +describe('pickupUpdatedMappings', () => { + let esServer: TestElasticsearchUtils['es']; + + beforeAll(async () => { + esServer = await startElasticsearch(); + }); + + beforeEach(async () => { + await createBaseline(); + await clearLog(); + }); + + describe('when performing a reindexing migration', () => { + it('should pickup all documents from the index', async () => { + const { runMigrations } = await getIncompatibleMappingsMigrator(); + + await runMigrations(); + + const logs = await parseLogFile(defaultLogFilePath); + + expect(logs).not.toContainLogEntry( + 'Kibana is performing a compatible upgrade and NO root fields have been udpated. Kibana will update the following SO types so that ES can pickup the updated mappings' + ); + }); + }); + + describe('when performing a compatible migration', () => { + it('should pickup only the types that have been updated', async () => { + const { runMigrations } = await getCompatibleMappingsMigrator(); + + await runMigrations(); + + const logs = await parseLogFile(defaultLogFilePath); + + expect(logs).toContainLogEntry( + 'Kibana is performing a compatible upgrade and NO root fields have been udpated. Kibana will update the following SO types so that ES can pickup the updated mappings: complex.' + ); + }); + + it('should pickup ALL documents if any root fields have been updated', async () => { + const { runMigrations, client } = await getCompatibleMappingsMigrator(); + + // we tamper the baseline mappings to simulate some root fields changes + const baselineMappings = await client.indices.getMapping({ index: defaultKibanaIndex }); + const _meta = baselineMappings[`${defaultKibanaIndex}_${currentVersion}_001`].mappings + ._meta as IndexMappingMeta; + _meta.migrationMappingPropertyHashes!.namespace = + _meta.migrationMappingPropertyHashes!.namespace + '_tampered'; + await client.indices.putMapping({ index: defaultKibanaIndex, _meta }); + + await runMigrations(); + + const logs = await parseLogFile(defaultLogFilePath); + + expect(logs).toContainLogEntry( + 'Kibana is performing a compatible upgrade and the mappings of some root fields have been changed. For Elasticsearch to pickup these mappings, all saved objects need to be updated. Updated root fields: namespace.' + ); + expect(logs).not.toContainLogEntry( + 'Kibana is performing a compatible upgrade and NO root fields have been udpated. Kibana will update the following SO types so that ES can pickup the updated mappings' + ); + }); + }); + + afterAll(async () => { + await esServer?.stop(); + await delay(2); + }); +}); diff --git a/src/core/server/integration_tests/saved_objects/migrations/kibana_migrator_test_kit.ts b/src/core/server/integration_tests/saved_objects/migrations/kibana_migrator_test_kit.ts index 57258aef0916e..26d1b2372a920 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/kibana_migrator_test_kit.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/kibana_migrator_test_kit.ts @@ -387,6 +387,16 @@ export const createBaseline = async () => { types: baselineTypes, }); + // remove the testing index (current and next minor) + await client.indices.delete({ + index: [ + defaultKibanaIndex, + `${defaultKibanaIndex}_${currentVersion}_001`, + `${defaultKibanaIndex}_${nextMinor}_001`, + ], + ignore_unavailable: true, + }); + await runMigrations(); await savedObjectsRepository.bulkCreate(baselineDocuments, { diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_top_nav.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_top_nav.tsx index 512c8b5fe7ebc..5e80bdf3ec97a 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_top_nav.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_top_nav.tsx @@ -17,7 +17,7 @@ import { import { ViewMode } from '@kbn/embeddable-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/public'; -import { EuiHorizontalRule, EuiToolTipProps } from '@elastic/eui'; +import { EuiHorizontalRule, EuiIcon, EuiToolTipProps } from '@elastic/eui'; import { getDashboardTitle, leaveConfirmStrings, @@ -145,10 +145,23 @@ export function DashboardTopNav({ embedSettings, redirectTo }: DashboardTopNavPr }, }, { - text: dashboardTitle, + text: + viewMode === ViewMode.EDIT ? ( + <> + {dashboardTitle} + + ) : ( + dashboardTitle + ), + onClick: + viewMode === ViewMode.EDIT + ? () => { + dashboard.showSettings(); + } + : undefined, }, ]); - }, [setBreadcrumbs, redirectTo, dashboardTitle]); + }, [setBreadcrumbs, redirectTo, dashboardTitle, dashboard, viewMode]); /** * Build app leave handler whenever hasUnsavedChanges changes diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/api/lib/extract_title_and_count.test.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/api/lib/extract_title_and_count.test.ts new file mode 100644 index 0000000000000..a781d1d74ee17 --- /dev/null +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/api/lib/extract_title_and_count.test.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { extractTitleAndCount } from './extract_title_and_count'; + +describe('extractTitleAndCount', () => { + it('extracts base title and copy count from a cloned dashboard title', () => { + expect(extractTitleAndCount('Test dashboard (1)')).toEqual(['Test dashboard', 1]); + expect(extractTitleAndCount('Test dashboard (2)')).toEqual(['Test dashboard', 2]); + expect(extractTitleAndCount('Test dashboard (200)')).toEqual(['Test dashboard', 200]); + expect(extractTitleAndCount('Test dashboard (1) (2) (3) (4) (5)')).toEqual([ + 'Test dashboard (1) (2) (3) (4)', + 5, + ]); + }); + + it('defaults to the count to 1 and returns the original title when the provided title does not contain a valid count', () => { + expect(extractTitleAndCount('Test dashboard')).toEqual(['Test dashboard', 1]); + expect(extractTitleAndCount('Test dashboard 2')).toEqual(['Test dashboard 2', 1]); + expect(extractTitleAndCount('Test dashboard (-1)')).toEqual(['Test dashboard (-1)', 1]); + expect(extractTitleAndCount('Test dashboard (0)')).toEqual(['Test dashboard (0)', 1]); + expect(extractTitleAndCount('Test dashboard (3.0)')).toEqual(['Test dashboard (3.0)', 1]); + expect(extractTitleAndCount('Test dashboard (8.4)')).toEqual(['Test dashboard (8.4)', 1]); + expect(extractTitleAndCount('Test dashboard (foo3.0)')).toEqual(['Test dashboard (foo3.0)', 1]); + expect(extractTitleAndCount('Test dashboard (bar7)')).toEqual(['Test dashboard (bar7)', 1]); + }); +}); diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/api/lib/extract_title_and_count.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/api/lib/extract_title_and_count.ts new file mode 100644 index 0000000000000..7100fab8af96d --- /dev/null +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/api/lib/extract_title_and_count.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 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 extractTitleAndCount = (title: string): [string, number] => { + if (title.slice(-1) === ')') { + const startIndex = title.lastIndexOf(' ('); + const count = title.substring(startIndex + 2, title.lastIndexOf(')')); + if (!count.includes('.') && Number.isInteger(Number(count)) && Number(count) >= 1) { + const baseTitle = title.substring(0, startIndex); + return [baseTitle, Number(count)]; + } + } + return [title, 1]; +}; diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/api/overlays/__snapshots__/clone_modal.test.js.snap b/src/plugins/dashboard/public/dashboard_container/embeddable/api/overlays/__snapshots__/clone_modal.test.js.snap deleted file mode 100644 index 6c00faa6546d2..0000000000000 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/api/overlays/__snapshots__/clone_modal.test.js.snap +++ /dev/null @@ -1,65 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders DashboardCloneModal 1`] = ` - - - - - - - - -

- -

-
- - -
- - - - - - - - -
-`; diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/api/overlays/clone_modal.test.js b/src/plugins/dashboard/public/dashboard_container/embeddable/api/overlays/clone_modal.test.js deleted file mode 100644 index bc88218a8e388..0000000000000 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/api/overlays/clone_modal.test.js +++ /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 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 sinon from 'sinon'; -import { shallowWithI18nProvider, mountWithI18nProvider } from '@kbn/test-jest-helpers'; -import { findTestSubject } from '@elastic/eui/lib/test'; - -import { DashboardCloneModal } from './clone_modal'; - -let onClone; -let onClose; - -beforeEach(() => { - onClone = sinon.spy(); - onClose = sinon.spy(); -}); - -test('renders DashboardCloneModal', () => { - const component = shallowWithI18nProvider( - - ); - expect(component).toMatchSnapshot(); -}); - -test('onClone', () => { - const component = mountWithI18nProvider( - - ); - findTestSubject(component, 'cloneConfirmButton').simulate('click'); - sinon.assert.calledWith(onClone, 'dash title'); - sinon.assert.notCalled(onClose); -}); - -test('onClose', () => { - const component = mountWithI18nProvider( - - ); - findTestSubject(component, 'cloneCancelButton').simulate('click'); - sinon.assert.calledOnce(onClose); - sinon.assert.notCalled(onClone); -}); - -test('title', () => { - const component = mountWithI18nProvider( - - ); - const event = { target: { value: 'a' } }; - component.find('input').simulate('change', event); - findTestSubject(component, 'cloneConfirmButton').simulate('click'); - sinon.assert.calledWith(onClone, 'a'); -}); diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/api/overlays/clone_modal.tsx b/src/plugins/dashboard/public/dashboard_container/embeddable/api/overlays/clone_modal.tsx deleted file mode 100644 index 0408b85d27fef..0000000000000 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/api/overlays/clone_modal.tsx +++ /dev/null @@ -1,203 +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 React, { Fragment } from 'react'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; - -import { - EuiButton, - EuiButtonEmpty, - EuiFieldText, - EuiModal, - EuiModalBody, - EuiModalFooter, - EuiModalHeader, - EuiModalHeaderTitle, - EuiSpacer, - EuiText, - EuiCallOut, -} from '@elastic/eui'; - -interface Props { - onClone: ( - newTitle: string, - isTitleDuplicateConfirmed: boolean, - onTitleDuplicate: () => void - ) => Promise; - onClose: () => void; - title: string; -} - -interface State { - newDashboardName: string; - isTitleDuplicateConfirmed: boolean; - hasTitleDuplicate: boolean; - isLoading: boolean; -} - -export class DashboardCloneModal extends React.Component { - private isMounted = false; - - constructor(props: Props) { - super(props); - - this.state = { - newDashboardName: props.title, - isTitleDuplicateConfirmed: false, - hasTitleDuplicate: false, - isLoading: false, - }; - } - componentDidMount() { - this.isMounted = true; - } - - componentWillUnmount() { - this.isMounted = false; - } - - onTitleDuplicate = () => { - this.setState({ - isTitleDuplicateConfirmed: true, - hasTitleDuplicate: true, - }); - }; - - cloneDashboard = async () => { - this.setState({ - isLoading: true, - }); - - await this.props.onClone( - this.state.newDashboardName, - this.state.isTitleDuplicateConfirmed, - this.onTitleDuplicate - ); - - if (this.isMounted) { - this.setState({ - isLoading: false, - }); - } - }; - - onInputChange = (event: any) => { - this.setState({ - newDashboardName: event.target.value, - isTitleDuplicateConfirmed: false, - hasTitleDuplicate: false, - }); - }; - - renderDuplicateTitleCallout = () => { - if (!this.state.hasTitleDuplicate) { - return; - } - - return ( - - - -

- - - - ), - }} - /> -

-
-
- ); - }; - - render() { - return ( - - - - - - - - - -

- -

-
- - - - - - {this.renderDuplicateTitleCallout()} -
- - - - - - - - - - -
- ); - } -} diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/api/overlays/show_clone_modal.tsx b/src/plugins/dashboard/public/dashboard_container/embeddable/api/overlays/show_clone_modal.tsx deleted file mode 100644 index 6ebd6f7711717..0000000000000 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/api/overlays/show_clone_modal.tsx +++ /dev/null @@ -1,72 +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 React from 'react'; -import { i18n } from '@kbn/i18n'; -import ReactDOM from 'react-dom'; - -import { I18nProvider } from '@kbn/i18n-react'; -import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; - -import { DashboardCloneModal } from './clone_modal'; -import { pluginServices } from '../../../../services/plugin_services'; - -export interface ShowCloneModalProps { - onClose: () => void; - onClone: ( - newTitle: string, - isTitleDuplicateConfirmed: boolean, - onTitleDuplicate: () => void - ) => Promise<{ id?: string } | { error: Error }>; - title: string; -} - -export function showCloneModal({ onClone, title, onClose }: ShowCloneModalProps) { - const { - settings: { theme }, - } = pluginServices.getServices(); - - const container = document.createElement('div'); - const closeModal = () => { - ReactDOM.unmountComponentAtNode(container); - document.body.removeChild(container); - onClose(); - }; - - const onCloneConfirmed = async ( - newTitle: string, - isTitleDuplicateConfirmed: boolean, - onTitleDuplicate: () => void - ) => { - onClone(newTitle, isTitleDuplicateConfirmed, onTitleDuplicate).then( - (response: { id?: string } | { error: Error }) => { - // The only time you don't want to close the modal is if it's asking you - // to confirm a duplicate title, in which case there will be no error and no id. - if ((response as { error: Error }).error || (response as { id?: string }).id) { - closeModal(); - } - } - ); - }; - document.body.appendChild(container); - const element = ( - - - - - - ); - ReactDOM.render(element, container); -} diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/api/run_save_functions.tsx b/src/plugins/dashboard/public/dashboard_container/embeddable/api/run_save_functions.tsx index 09eba955766c2..ae13c70a739d6 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/api/run_save_functions.tsx +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/api/run_save_functions.tsx @@ -15,10 +15,10 @@ import { DASHBOARD_CONTENT_ID, SAVED_OBJECT_POST_TIME } from '../../../dashboard import { DashboardSaveOptions, DashboardStateFromSaveModal } from '../../types'; import { DashboardSaveModal } from './overlays/save_modal'; import { DashboardContainer } from '../dashboard_container'; -import { showCloneModal } from './overlays/show_clone_modal'; import { pluginServices } from '../../../services/plugin_services'; import { DashboardContainerInput } from '../../../../common'; import { SaveDashboardReturn } from '../../../services/dashboard_content_management/types'; +import { extractTitleAndCount } from './lib/extract_title_and_count'; export function runSaveAs(this: DashboardContainer) { const { @@ -152,31 +152,41 @@ export async function runClone(this: DashboardContainer) { const { explicitInput: currentState } = this.getState(); - return new Promise((resolve) => { - const onClone = async ( - newTitle: string, - isTitleDuplicateConfirmed: boolean, - onTitleDuplicate: () => void - ) => { - if ( + return new Promise(async (resolve, reject) => { + try { + const [baseTitle, baseCount] = extractTitleAndCount(currentState.title); + let copyCount = baseCount; + let newTitle = `${baseTitle} (${copyCount})`; + while ( !(await checkForDuplicateDashboardTitle({ title: newTitle, - onTitleDuplicate, lastSavedTitle: currentState.title, copyOnSave: true, - isTitleDuplicateConfirmed, + isTitleDuplicateConfirmed: false, })) ) { - // do not clone if title is duplicate and is unconfirmed - return {}; + copyCount++; + newTitle = `${baseTitle} (${copyCount})`; } const saveResult = await saveDashboardState({ - saveOptions: { saveAsCopy: true }, - currentState: { ...currentState, title: newTitle }, + saveOptions: { + saveAsCopy: true, + }, + currentState: { + ...currentState, + title: newTitle, + }, }); resolve(saveResult); - return saveResult.id ? { id: saveResult.id } : { error: saveResult.error }; - }; - showCloneModal({ onClone, title: currentState.title, onClose: () => resolve(undefined) }); + return saveResult.id + ? { + id: saveResult.id, + } + : { + error: saveResult.error, + }; + } catch (error) { + reject(error); + } }); } diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.test.tsx b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.test.tsx index 5a360446f03a8..b22c791926574 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.test.tsx +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.test.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { mount } from 'enzyme'; +import type { TimeRange } from '@kbn/es-query'; import { mockedReduxEmbeddablePackage } from '@kbn/presentation-util-plugin/public/mocks'; import { findTestSubject, nextTick } from '@kbn/test-jest-helpers'; import { I18nProvider } from '@kbn/i18n-react'; @@ -255,3 +256,60 @@ test('DashboardContainer in edit mode shows edit mode actions', async () => { // const action = findTestSubject(component, `embeddablePanelAction-${editModeAction.id}`); // expect(action.length).toBe(1); }); + +describe('getInheritedInput', () => { + const dashboardTimeRange = { + to: 'now', + from: 'now-15m', + }; + const dashboardTimeslice = [1688061910000, 1688062209000] as [number, number]; + + test('Should pass dashboard timeRange and timeslice to panel when panel does not have custom time range', async () => { + const container = buildMockDashboard({ + timeRange: dashboardTimeRange, + timeslice: dashboardTimeslice, + }); + const embeddable = await container.addNewEmbeddable( + CONTACT_CARD_EMBEDDABLE, + { + firstName: 'Kibana', + } + ); + expect(embeddable).toBeDefined(); + + const embeddableInput = container + .getChild(embeddable.id) + .getInput() as ContactCardEmbeddableInput & { + timeRange: TimeRange; + timeslice: [number, number]; + }; + expect(embeddableInput.timeRange).toEqual(dashboardTimeRange); + expect(embeddableInput.timeslice).toEqual(dashboardTimeslice); + }); + + test('Should not pass dashboard timeRange and timeslice to panel when panel has custom time range', async () => { + const container = buildMockDashboard({ + timeRange: dashboardTimeRange, + timeslice: dashboardTimeslice, + }); + const embeddableTimeRange = { + to: 'now', + from: 'now-24h', + }; + const embeddable = await container.addNewEmbeddable< + ContactCardEmbeddableInput & { timeRange: TimeRange } + >(CONTACT_CARD_EMBEDDABLE, { + firstName: 'Kibana', + timeRange: embeddableTimeRange, + }); + + const embeddableInput = container + .getChild(embeddable.id) + .getInput() as ContactCardEmbeddableInput & { + timeRange: TimeRange; + timeslice: [number, number]; + }; + expect(embeddableInput.timeRange).toEqual(embeddableTimeRange); + expect(embeddableInput.timeslice).toBeUndefined(); + }); +}); diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx index df49e77171053..40b67faf67ed6 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx @@ -259,12 +259,16 @@ export class DashboardContainer extends Container)?.timeRange + ); return { searchSessionId: this.searchSessionId, refreshConfig: refreshInterval, @@ -273,11 +277,13 @@ export class DashboardContainer extends Container void; + onTitleDuplicate?: () => void; isTitleDuplicateConfirmed: boolean; } diff --git a/src/plugins/discover/public/application/main/services/discover_state.test.ts b/src/plugins/discover/public/application/main/services/discover_state.test.ts index bcf9aef7d80f1..0316ddd1b383d 100644 --- a/src/plugins/discover/public/application/main/services/discover_state.test.ts +++ b/src/plugins/discover/public/application/main/services/discover_state.test.ts @@ -183,18 +183,21 @@ describe('Test createSearchSessionRestorationDataProvider', () => { let mockSavedSearch: SavedSearch = {} as unknown as SavedSearch; const history = createBrowserHistory(); const mockDataPlugin = dataPluginMock.createStartContract(); + const discoverStateContainer = getDiscoverStateContainer({ + services: discoverServiceMock, + history, + }); + discoverStateContainer.appState.update({ + index: savedSearchMock.searchSource.getField('index')!.id, + }); const searchSessionInfoProvider = createSearchSessionRestorationDataProvider({ data: mockDataPlugin, - appStateContainer: getDiscoverStateContainer({ - savedSearch: savedSearchMock, - services: discoverServiceMock, - history, - }).appState, + appStateContainer: discoverStateContainer.appState, getSavedSearch: () => mockSavedSearch, }); describe('session name', () => { - test('No saved search returns default name', async () => { + test('No persisted saved search returns default name', async () => { expect(await searchSessionInfoProvider.getName()).toBe('Discover'); }); @@ -211,6 +214,7 @@ describe('Test createSearchSessionRestorationDataProvider', () => { describe('session state', () => { test('restoreState has sessionId and initialState has not', async () => { + mockSavedSearch = savedSearchMock; const searchSessionId = 'id'; (mockDataPlugin.search.session.getSessionId as jest.Mock).mockImplementation( () => searchSessionId @@ -221,6 +225,7 @@ describe('Test createSearchSessionRestorationDataProvider', () => { }); test('restoreState has absoluteTimeRange', async () => { + mockSavedSearch = savedSearchMock; const relativeTime = 'relativeTime'; const absoluteTime = 'absoluteTime'; (mockDataPlugin.query.timefilter.timefilter.getTime as jest.Mock).mockImplementation( @@ -235,6 +240,7 @@ describe('Test createSearchSessionRestorationDataProvider', () => { }); test('restoreState has paused autoRefresh', async () => { + mockSavedSearch = savedSearchMock; const { initialState, restoreState } = await searchSessionInfoProvider.getLocatorData(); expect(initialState.refreshInterval).toBe(undefined); expect(restoreState.refreshInterval).toEqual({ @@ -242,6 +248,21 @@ describe('Test createSearchSessionRestorationDataProvider', () => { value: 0, }); }); + + test('restoreState has persisted data view', async () => { + mockSavedSearch = savedSearchMock; + const { initialState, restoreState } = await searchSessionInfoProvider.getLocatorData(); + expect(initialState.dataViewSpec).toEqual(undefined); + expect(restoreState.dataViewSpec).toEqual(undefined); + expect(initialState.dataViewId).toEqual(savedSearchMock.searchSource.getField('index')?.id); + }); + + test('restoreState has temporary data view', async () => { + mockSavedSearch = savedSearchAdHoc; + const { initialState, restoreState } = await searchSessionInfoProvider.getLocatorData(); + expect(initialState.dataViewSpec).toEqual({}); + expect(restoreState.dataViewSpec).toEqual({}); + }); }); }); diff --git a/src/plugins/discover/public/application/main/services/discover_state.ts b/src/plugins/discover/public/application/main/services/discover_state.ts index 79eea9eed9964..e80f6e5c338b7 100644 --- a/src/plugins/discover/public/application/main/services/discover_state.ts +++ b/src/plugins/discover/public/application/main/services/discover_state.ts @@ -476,7 +476,7 @@ export function createSearchSessionRestorationDataProvider(deps: { data: DataPublicPluginStart; getSavedSearch: () => SavedSearch; }): SearchSessionInfoProvider { - const getSavedSearchId = () => deps.getSavedSearch().id; + const getSavedSearch = () => deps.getSavedSearch(); return { getName: async () => { const savedSearch = deps.getSavedSearch(); @@ -492,12 +492,12 @@ export function createSearchSessionRestorationDataProvider(deps: { id: DISCOVER_APP_LOCATOR, initialState: createUrlGeneratorState({ ...deps, - getSavedSearchId, + getSavedSearch, shouldRestoreSearchSession: false, }), restoreState: createUrlGeneratorState({ ...deps, - getSavedSearchId, + getSavedSearch, shouldRestoreSearchSession: true, }), }; @@ -508,20 +508,21 @@ export function createSearchSessionRestorationDataProvider(deps: { function createUrlGeneratorState({ appStateContainer, data, - getSavedSearchId, + getSavedSearch, shouldRestoreSearchSession, }: { appStateContainer: StateContainer; data: DataPublicPluginStart; - getSavedSearchId: () => string | undefined; + getSavedSearch: () => SavedSearch; shouldRestoreSearchSession: boolean; }): DiscoverAppLocatorParams { const appState = appStateContainer.get(); + const dataView = getSavedSearch().searchSource.getField('index'); return { filters: data.query.filterManager.getFilters(), dataViewId: appState.index, query: appState.query, - savedSearchId: getSavedSearchId(), + savedSearchId: getSavedSearch().id, timeRange: shouldRestoreSearchSession ? data.query.timefilter.timefilter.getAbsoluteTime() : data.query.timefilter.timefilter.getTime(), @@ -540,5 +541,6 @@ function createUrlGeneratorState({ viewMode: appState.viewMode, hideAggregatedPreview: appState.hideAggregatedPreview, breakdownField: appState.breakdownField, + dataViewSpec: !dataView?.isPersisted() ? dataView?.toSpec(false) : undefined, }; } diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid.test.tsx b/src/plugins/discover/public/components/discover_grid/discover_grid.test.tsx index c6d0bb4e0f48b..bd6529dd44314 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid.test.tsx +++ b/src/plugins/discover/public/components/discover_grid/discover_grid.test.tsx @@ -275,42 +275,6 @@ describe('DiscoverGrid', () => { }) ); }); - - it('should call useDataGridColumnsCellActions with empty field name and type for unsupported field types', async () => { - await getComponent({ - ...getProps(), - columns: ['message', '_source'], - onFieldEdited: jest.fn(), - cellActionsTriggerId: 'test', - }); - - expect(mockUseDataGridColumnsCellActions).toHaveBeenCalledWith( - expect.objectContaining({ - triggerId: 'test', - getCellValue: expect.any(Function), - fields: [ - { - name: '@timestamp', - type: 'date', - aggregatable: true, - searchable: undefined, - }, - { - name: 'message', - type: 'string', - aggregatable: false, - searchable: undefined, - }, - { - searchable: false, - aggregatable: false, - name: '', - type: '', - }, - ], - }) - ); - }); }); describe('sorting', () => { 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 49d4f61def707..3d4acfc34d925 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid.tsx +++ b/src/plugins/discover/public/components/discover_grid/discover_grid.tsx @@ -29,14 +29,12 @@ import type { SortOrder } from '@kbn/saved-search-plugin/public'; import { useDataGridColumnsCellActions, type UseDataGridColumnsCellActionsProps, - type CellActionFieldValue, } from '@kbn/cell-actions'; import type { AggregateQuery, Filter, Query } from '@kbn/es-query'; import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import type { ToastsStart, IUiSettingsClient, HttpStart, CoreStart } from '@kbn/core/public'; import { DataViewFieldEditorStart } from '@kbn/data-view-field-editor-plugin/public'; -import { KBN_FIELD_TYPES } from '@kbn/data-plugin/common'; -import { isTypeSupportedByCellActions } from '@kbn/cell-actions/src/utils'; +import { Serializable } from '@kbn/utility-types'; import { DocViewFilterFn } from '../../services/doc_views/doc_views_types'; import { getSchemaDetectors } from './discover_grid_schema'; import { DiscoverGridFlyout } from './discover_grid_flyout'; @@ -451,7 +449,7 @@ export const DiscoverGrid = ({ const getCellValue = useCallback( (fieldName, rowIndex) => - displayedRows[rowIndex % displayedRows.length].flattened[fieldName] as CellActionFieldValue, + displayedRows[rowIndex % displayedRows.length].flattened[fieldName] as Serializable, [displayedRows] ); @@ -460,8 +458,7 @@ export const DiscoverGrid = ({ cellActionsTriggerId && !isPlainRecord ? visibleColumns.map((columnName) => { const field = dataView.getFieldByName(columnName); - if (!field || !isTypeSupportedByCellActions(field.type as KBN_FIELD_TYPES)) { - // disable custom actions on object columns + if (!field) { return { name: '', type: '', diff --git a/src/plugins/files/server/blob_storage_service/adapters/es/content_stream/content_stream.test.ts b/src/plugins/files/server/blob_storage_service/adapters/es/content_stream/content_stream.test.ts index 4bae40076e581..cbd29e6bf4c18 100644 --- a/src/plugins/files/server/blob_storage_service/adapters/es/content_stream/content_stream.test.ts +++ b/src/plugins/files/server/blob_storage_service/adapters/es/content_stream/content_stream.test.ts @@ -15,6 +15,8 @@ import { ContentStream, ContentStreamEncoding, ContentStreamParameters } from '. import type { GetResponse } from '@elastic/elasticsearch/lib/api/types'; import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { FileDocument } from '../../../../file_client/file_metadata_client/adapters/es_index'; +import * as cborx from 'cbor-x'; +import { IndexRequest } from '@elastic/elasticsearch/lib/api/types'; describe('ContentStream', () => { let client: ReturnType; @@ -282,12 +284,14 @@ describe('ContentStream', () => { }); it('should emit an error event', async () => { - client.index.mockRejectedValueOnce('some error'); + client.index.mockRejectedValueOnce(new Error('some error')); stream.end('data'); const error = await new Promise((resolve) => stream.once('error', resolve)); - expect(error).toBe('some error'); + expect((error as Error).toString()).toEqual( + 'FilesPluginError: ContentStream.indexChunk(): some error' + ); }); it('should remove all previous chunks before writing', async () => { @@ -405,5 +409,15 @@ describe('ContentStream', () => { expect(deleteRequest).toHaveProperty('query.bool.must.match.bid', 'something'); }); + + it('should write @timestamp if `indexIsAlias` is true', async () => { + stream = new ContentStream(client, undefined, 'somewhere', logger, undefined, true); + stream.end('some data'); + await new Promise((resolve) => stream.once('finish', resolve)); + const docBuffer = (client.index.mock.calls[0][0] as IndexRequest).document as Buffer; + const docData = cborx.decode(docBuffer); + + expect(docData).toHaveProperty('@timestamp'); + }); }); }); diff --git a/src/plugins/files/server/blob_storage_service/adapters/es/content_stream/content_stream.ts b/src/plugins/files/server/blob_storage_service/adapters/es/content_stream/content_stream.ts index 98aebda3c7735..aeb547a1bf6f8 100644 --- a/src/plugins/files/server/blob_storage_service/adapters/es/content_stream/content_stream.ts +++ b/src/plugins/files/server/blob_storage_service/adapters/es/content_stream/content_stream.ts @@ -16,6 +16,7 @@ import { Duplex, Writable, Readable } from 'stream'; import { GetResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { inspect } from 'util'; +import { wrapErrorAndReThrow } from '../../../../file_client/utils'; import type { FileChunkDocument } from '../mappings'; type Callback = (error?: Error) => void; @@ -238,27 +239,29 @@ export class ContentStream extends Duplex { } private async indexChunk({ bid, data, id, index }: IndexRequestParams, last?: true) { - await this.client.index( - { - id, - index, - document: cborx.encode( - last - ? { - data, - bid, - last, - } - : { data, bid } - ), - }, - { - headers: { - 'content-type': 'application/cbor', - accept: 'application/json', + await this.client + .index( + { + id, + index, + op_type: 'create', + document: cborx.encode({ + data, + bid, + // Mark it as last? + ...(last ? { last } : {}), + // Add `@timestamp` for Index Alias/DS? + ...(this.indexIsAlias ? { '@timestamp': new Date().toISOString() } : {}), + }), }, - } - ); + { + headers: { + 'content-type': 'application/cbor', + accept: 'application/json', + }, + } + ) + .catch(wrapErrorAndReThrow.withMessagePrefix('ContentStream.indexChunk(): ')); } /** diff --git a/src/plugins/files/server/blob_storage_service/adapters/es/es.test.ts b/src/plugins/files/server/blob_storage_service/adapters/es/es.test.ts index bc2cbe0870d59..d94b2c78c5885 100644 --- a/src/plugins/files/server/blob_storage_service/adapters/es/es.test.ts +++ b/src/plugins/files/server/blob_storage_service/adapters/es/es.test.ts @@ -8,35 +8,51 @@ import { Readable } from 'stream'; import { promisify } from 'util'; -import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; import { Semaphore } from '@kbn/std'; import { ElasticsearchBlobStorageClient } from './es'; +import { errors } from '@elastic/elasticsearch'; const setImmediate = promisify(global.setImmediate); describe('ElasticsearchBlobStorageClient', () => { - let esClient: ElasticsearchClient; - let blobStoreClient: ElasticsearchBlobStorageClient; + let esClient: ReturnType; let semaphore: Semaphore; + let logger: ReturnType; - beforeEach(() => { - semaphore = new Semaphore(1); - esClient = elasticsearchServiceMock.createElasticsearchClient(); - blobStoreClient = new ElasticsearchBlobStorageClient( + // Exposed `clearCache()` which resets the cache for the memoized `createIndexIfNotExists()` method + class ElasticsearchBlobStorageClientWithCacheClear extends ElasticsearchBlobStorageClient { + static clearCache() { + // @ts-expect-error TS2722: Cannot invoke an object which is possibly 'undefined' (??) + this.createIndexIfNotExists.cache.clear(); + } + } + + const createBlobStoreClient = (index?: string, indexIsAlias: boolean = false) => { + ElasticsearchBlobStorageClientWithCacheClear.clearCache(); + + return new ElasticsearchBlobStorageClientWithCacheClear( esClient, + index, undefined, - undefined, - loggingSystemMock.createLogger(), - semaphore + logger, + semaphore, + indexIsAlias ); + }; + + beforeEach(() => { + semaphore = new Semaphore(1); + logger = loggingSystemMock.createLogger(); + esClient = elasticsearchServiceMock.createElasticsearchClient(); }); test('limits max concurrent uploads', async () => { + const blobStoreClient = createBlobStoreClient(); const acquireSpy = jest.spyOn(semaphore, 'acquire'); - (esClient.index as jest.Mock).mockImplementation(() => { + esClient.index.mockImplementation(() => { return new Promise((res, rej) => setTimeout(() => rej('failed'), 100)); }); const [p1, p2, ...rest] = [ @@ -54,4 +70,59 @@ describe('ElasticsearchBlobStorageClient', () => { await Promise.all(rest); expect(esClient.index).toHaveBeenCalledTimes(4); }); + + describe('.createIndexIfNotExists()', () => { + let data: Readable; + + beforeEach(() => { + data = Readable.from(['test']); + }); + + it('should create index if it does not exist', async () => { + esClient.indices.exists.mockResolvedValue(false); + const blobStoreClient = await createBlobStoreClient('foo1'); + + await blobStoreClient.upload(data); + expect(logger.info).toHaveBeenCalledWith( + 'Creating [foo1] index for Elasticsearch blob store.' + ); + + // Calling a second time should do nothing + logger.info.mockClear(); + await blobStoreClient.upload(data); + + expect(logger.info).not.toHaveBeenCalledWith( + 'Creating [foo1] index for Elasticsearch blob store.' + ); + }); + + it('should not create index if it already exists', async () => { + esClient.indices.exists.mockResolvedValue(true); + await createBlobStoreClient('foo1').upload(data); + + expect(logger.debug).toHaveBeenCalledWith('[foo1] already exists. Nothing to do'); + }); + + it('should not create index if `indexIsAlias` is `true`', async () => { + await createBlobStoreClient('foo1', true).upload(data); + + expect(logger.debug).toHaveBeenCalledWith( + 'No need to create index [foo1] as it is an Alias or DS.' + ); + }); + + it('should not reject if it is unable to create the index (best effort)', async () => { + esClient.indices.exists.mockResolvedValue(false); + esClient.indices.create.mockRejectedValue( + new errors.ResponseError({ + statusCode: 400, + } as ConstructorParameters[0]) + ); + await createBlobStoreClient('foo1', false).upload(data); + + expect(logger.warn).toHaveBeenCalledWith( + 'Unable to create blob storage index [foo1], it may have been created already.' + ); + }); + }); }); diff --git a/src/plugins/files/server/blob_storage_service/adapters/es/es.ts b/src/plugins/files/server/blob_storage_service/adapters/es/es.ts index 3b279fba10335..106b7251fed23 100644 --- a/src/plugins/files/server/blob_storage_service/adapters/es/es.ts +++ b/src/plugins/files/server/blob_storage_service/adapters/es/es.ts @@ -7,7 +7,6 @@ */ import assert from 'assert'; -import { once } from 'lodash'; import { errors } from '@elastic/elasticsearch'; import type { ElasticsearchClient, Logger } from '@kbn/core/server'; import { Semaphore } from '@kbn/std'; @@ -16,6 +15,7 @@ import { pipeline } from 'stream/promises'; import { promisify } from 'util'; import { lastValueFrom, defer } from 'rxjs'; import { PerformanceMetricEvent, reportPerformanceMetricEvent } from '@kbn/ebt-tools'; +import { memoize } from 'lodash'; import { FilesPlugin } from '../../../plugin'; import { FILE_UPLOAD_PERFORMANCE_EVENT_NAME } from '../../../performance'; import type { BlobStorageClient } from '../../types'; @@ -65,23 +65,29 @@ export class ElasticsearchBlobStorageClient implements BlobStorageClient { } /** - * This function acts as a singleton i.t.o. execution: it can only be called once. - * Subsequent calls should not re-execute it. - * - * There is a known issue where calling this function simultaneously can result - * in a race condition where one of the calls will fail because the index is already - * being created. This is only an issue for the very first time the index is being - * created. + * This function acts as a singleton i.t.o. execution: it can only be called once per index. + * Subsequent calls for the same index should not re-execute it. */ - private static createIndexIfNotExists = once( - async (index: string, esClient: ElasticsearchClient, logger: Logger): Promise => { + protected static createIndexIfNotExists = memoize( + async ( + index: string, + esClient: ElasticsearchClient, + logger: Logger, + indexIsAlias: boolean + ): Promise => { + // We don't attempt to create the index if it is an Alias/DS + if (indexIsAlias) { + logger.debug(`No need to create index [${index}] as it is an Alias or DS.`); + return; + } + try { if (await esClient.indices.exists({ index })) { - logger.debug(`${index} already exists.`); + logger.debug(`[${index}] already exists. Nothing to do`); return; } - logger.info(`Creating ${index} for Elasticsearch blob store.`); + logger.info(`Creating [${index}] index for Elasticsearch blob store.`); await esClient.indices.create({ index, @@ -96,7 +102,9 @@ export class ElasticsearchBlobStorageClient implements BlobStorageClient { }); } catch (e) { if (e instanceof errors.ResponseError && e.statusCode === 400) { - logger.warn('Unable to create blob storage index, it may have been created already.'); + logger.warn( + `Unable to create blob storage index [${index}], it may have been created already.` + ); } // best effort } @@ -109,7 +117,8 @@ export class ElasticsearchBlobStorageClient implements BlobStorageClient { await ElasticsearchBlobStorageClient.createIndexIfNotExists( this.index, this.esClient, - this.logger + this.logger, + this.indexIsAlias ); const processUpload = async () => { @@ -123,6 +132,7 @@ export class ElasticsearchBlobStorageClient implements BlobStorageClient { parameters: { maxChunkSize: this.chunkSize, }, + indexIsAlias: this.indexIsAlias, }); const start = performance.now(); @@ -183,6 +193,7 @@ export class ElasticsearchBlobStorageClient implements BlobStorageClient { client: this.esClient, index: this.index, logger: this.logger.get('content-stream-delete'), + indexIsAlias: this.indexIsAlias, }); /** @note Overwriting existing content with an empty buffer to remove all the chunks. */ await promisify(dest.end.bind(dest, '', 'utf8'))(); diff --git a/src/plugins/files/server/file/file.test.ts b/src/plugins/files/server/file/file.test.ts index 7fa062b85e2ec..89b0f46458c8d 100644 --- a/src/plugins/files/server/file/file.test.ts +++ b/src/plugins/files/server/file/file.test.ts @@ -29,6 +29,7 @@ import { FileMetadataClient } from '../file_client'; import { SavedObjectsFileMetadataClient } from '../file_client/file_metadata_client/adapters/saved_objects'; import { File as IFile } from '../../common'; import { createFileHashTransform } from '..'; +import { FilesPluginError } from '../file_client/utils'; const setImmediate = promisify(global.setImmediate); @@ -82,7 +83,9 @@ describe('File', () => { const [{ returnValue: blobStore }] = createBlobSpy.getCalls(); const blobStoreSpy = sandbox.spy(blobStore, 'delete'); expect(blobStoreSpy.calledOnce).toBe(false); - await expect(file.uploadContent(Readable.from(['test']))).rejects.toThrow(new Error('test')); + await expect(file.uploadContent(Readable.from(['test']))).rejects.toThrow( + new FilesPluginError('ContentStream.indexChunk(): test') + ); await setImmediate(); expect(blobStoreSpy.calledOnce).toBe(true); }); diff --git a/src/plugins/files/server/file_client/create_es_file_client.ts b/src/plugins/files/server/file_client/create_es_file_client.ts index dfe74947871a1..47b044618efc2 100644 --- a/src/plugins/files/server/file_client/create_es_file_client.ts +++ b/src/plugins/files/server/file_client/create_es_file_client.ts @@ -31,10 +31,11 @@ export interface CreateEsFileClientArgs { */ elasticsearchClient: ElasticsearchClient; /** - * Treat the indices provided as Aliases. If set to true, ES `search()` will be used to - * retrieve the file info and content instead of `get()`. This is needed to ensure the - * content can be retrieved in cases where an index may have rolled over (ES `get()` - * needs a "real" index) + * Treat the indices provided as Aliases/Datastreams. + * When set to `true`: + * - additional ES calls will be made to get the real backing indexes + * - will not check if indexes exists and attempt to create them if not + * - an additional `@timestamp` property will be written to all documents (at root of document) */ indexIsAlias?: boolean; /** diff --git a/src/plugins/files/server/file_client/file_metadata_client/adapters/es_index.test.ts b/src/plugins/files/server/file_client/file_metadata_client/adapters/es_index.test.ts new file mode 100644 index 0000000000000..833add2c4f72b --- /dev/null +++ b/src/plugins/files/server/file_client/file_metadata_client/adapters/es_index.test.ts @@ -0,0 +1,94 @@ +/* + * Copyright 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 { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks'; +import { Logger } from '@kbn/logging'; +import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; +import { EsIndexFilesMetadataClient } from '../..'; +import { FileMetadata } from '@kbn/shared-ux-file-types'; +import { estypes } from '@elastic/elasticsearch'; + +describe('EsIndexFilesMetadataClient', () => { + let esClient: ReturnType; + let logger: Logger; + + const generateMetadata = (): FileMetadata => { + return { + created: '2023-06-26T17:33:35.968Z', + Updated: '2023-06-26T17:33:35.968Z', + Status: 'READY', + name: 'lol.gif', + mime_type: 'image/gif', + extension: 'gif', + FileKind: 'none', + size: 134751, + }; + }; + + beforeEach(() => { + esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + logger = loggingSystemMock.createLogger(); + }); + + describe('and `indexIsAlias` prop is `true`', () => { + let metaClient: EsIndexFilesMetadataClient; + + beforeEach(() => { + metaClient = new EsIndexFilesMetadataClient('foo', esClient, logger, true); + }); + + it('should NOT create index', async () => { + esClient.index.mockResolvedValue({ _id: '123' } as estypes.WriteResponseBase); + await metaClient.create({ id: '123', metadata: generateMetadata() }); + + expect(logger.debug).toHaveBeenCalledWith( + 'No need to create index [foo] as it is an Alias or DS.' + ); + }); + + it('should retrieve backing index on update', async () => { + // For `.getBackingIndex()` + esClient.search.mockResolvedValueOnce({ + hits: { hits: [{ _index: 'foo-00001' } as estypes.SearchHit] }, + } as estypes.SearchResponse); + // For `.get()` + esClient.search.mockResolvedValueOnce({ + hits: { hits: [{ _source: { file: generateMetadata() } } as estypes.SearchHit] }, + } as estypes.SearchResponse); + + await metaClient.update({ id: '123', metadata: generateMetadata() }); + + expect(esClient.search).toHaveBeenCalledWith({ + body: { + _source: false, + query: { + term: { + _id: '123', + }, + }, + size: 1, + }, + index: 'foo', + }); + expect(esClient.update).toHaveBeenCalledWith(expect.objectContaining({ index: 'foo-00001' })); + }); + + it('should write @timestamp on create', async () => { + esClient.index.mockResolvedValue({ _id: '123' } as estypes.WriteResponseBase); + await metaClient.create({ id: '123', metadata: generateMetadata() }); + + expect(esClient.index).toHaveBeenCalledWith( + expect.objectContaining({ + document: expect.objectContaining({ + '@timestamp': expect.any(String), + }), + }) + ); + }); + }); +}); diff --git a/src/plugins/files/server/file_client/file_metadata_client/adapters/es_index.ts b/src/plugins/files/server/file_client/file_metadata_client/adapters/es_index.ts index fa3984fdbc876..37f8d3ddd6791 100644 --- a/src/plugins/files/server/file_client/file_metadata_client/adapters/es_index.ts +++ b/src/plugins/files/server/file_client/file_metadata_client/adapters/es_index.ts @@ -14,6 +14,7 @@ import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { MappingProperty, SearchTotalHits } from '@elastic/elasticsearch/lib/api/types'; import pLimit from 'p-limit'; +import { wrapErrorAndReThrow } from '../../utils'; import type { FilesMetrics, FileMetadata, Pagination } from '../../../../common'; import type { FindFileArgs } from '../../../file_service'; import type { @@ -41,6 +42,8 @@ const fileMappings: MappingProperty = { export interface FileDocument { file: FileMetadata; + /** Written only when `indexIsAlias` is `true` */ + '@timestamp'?: string; } export class EsIndexFilesMetadataClient implements FileMetadataClient { @@ -52,32 +55,91 @@ export class EsIndexFilesMetadataClient implements FileMetadataClie ) {} private createIfNotExists = once(async () => { + // We don't attempt to create the index if it is an Alias/DS + if (this.indexIsAlias) { + this.logger.debug(`No need to create index [${this.index}] as it is an Alias or DS.`); + return; + } + try { if (await this.esClient.indices.exists({ index: this.index })) { return; } - await this.esClient.indices.create({ - index: this.index, - mappings: { - dynamic: false, - properties: { - file: fileMappings, + + await this.esClient.indices + .create({ + index: this.index, + mappings: { + dynamic: false, + properties: { + file: fileMappings, + }, }, - }, - }); + }) + .catch( + wrapErrorAndReThrow.withMessagePrefix('EsIndexFilesMetadataClient.createIfNotExists(): ') + ); + + this.logger.info(`index [${this.index}] created with default mappings.`); } catch (e) { + this.logger.error(`Failed to create index [${this.index}]: ${e.message}`); + this.logger.debug(e); // best effort } }); + private async getBackingIndex(id: string): Promise { + if (!this.indexIsAlias) { + return this.index; + } + + const doc = await this.esClient + .search({ + index: this.index, + body: { + size: 1, + query: { + term: { + _id: id, + }, + }, + _source: false, // suppress the document content + }, + }) + .catch( + wrapErrorAndReThrow.withMessagePrefix('EsIndexFilesMetadataClient.getBackingIndex(): ') + ); + + const docIndex = doc.hits.hits?.[0]?._index; + + if (!docIndex) { + const err = new Error( + `Unable to determine backing index for file id [${id}] in index (alias) [${this.index}]` + ); + + this.logger.error(err); + throw err; + } + + return docIndex; + } + async create({ id, metadata }: FileDescriptor): Promise> { await this.createIfNotExists(); - const result = await this.esClient.index({ - index: this.index, - id, - document: { file: metadata }, - refresh: 'wait_for', - }); + const result = await this.esClient + .index({ + index: this.index, + id, + document: { + file: metadata, + // Add `@timestamp` if index is an Alias/DS + ...(this.indexIsAlias ? { '@timestamp': new Date().toISOString() } : {}), + }, + op_type: 'create', + refresh: 'wait_for', + }) + .catch(wrapErrorAndReThrow.withMessagePrefix('EsIndexFilesMetadataClient.create(): ')); + return { id: result._id, metadata, @@ -90,24 +152,28 @@ export class EsIndexFilesMetadataClient implements FileMetadataClie if (indexIsAlias) { doc = ( - await esClient.search>({ - index, - body: { - size: 1, - query: { - term: { - _id: id, + await esClient + .search>({ + index, + body: { + size: 1, + query: { + term: { + _id: id, + }, }, }, - }, - }) + }) + .catch(wrapErrorAndReThrow.withMessagePrefix('EsIndexFilesMetadataClient.get(): ')) ).hits.hits?.[0]?._source; } else { doc = ( - await esClient.get>({ - index, - id, - }) + await esClient + .get>({ + index, + id, + }) + .catch(wrapErrorAndReThrow.withMessagePrefix('EsIndexFilesMetadataClient.get(): ')) )._source; } @@ -141,16 +207,23 @@ export class EsIndexFilesMetadataClient implements FileMetadataClie } async delete({ id }: DeleteArg): Promise { - await this.esClient.delete({ index: this.index, id }); + await this.esClient + .delete({ index: this.index, id }) + .catch(wrapErrorAndReThrow.withMessagePrefix('EsIndexFilesMetadataClient.delete(): ')); } async update({ id, metadata }: UpdateArgs): Promise> { - await this.esClient.update({ - index: this.index, - id, - doc: { file: metadata }, - refresh: 'wait_for', - }); + const index = await this.getBackingIndex(id); + + await this.esClient + .update({ + index, + id, + doc: { file: metadata }, + refresh: 'wait_for', + }) + .catch(wrapErrorAndReThrow.withMessagePrefix('EsIndexFilesMetadataClient.update(): ')); + return this.get({ id }); } @@ -167,14 +240,16 @@ export class EsIndexFilesMetadataClient implements FileMetadataClie total: number; files: Array>; }> { - const result = await this.esClient.search>({ - track_total_hits: true, - index: this.index, - expand_wildcards: 'hidden', - query: filterArgsToESQuery({ ...filterArgs, attrPrefix: this.attrPrefix }), - ...this.paginationToES({ page, perPage }), - sort: 'file.created', - }); + const result = await this.esClient + .search>({ + track_total_hits: true, + index: this.index, + expand_wildcards: 'hidden', + query: filterArgsToESQuery({ ...filterArgs, attrPrefix: this.attrPrefix }), + ...this.paginationToES({ page, perPage }), + sort: 'file.created', + }) + .catch(wrapErrorAndReThrow.withMessagePrefix('EsIndexFilesMetadataClient.find(): ')); return { total: (result.hits.total as SearchTotalHits).value, diff --git a/src/plugins/files/server/file_client/utils.ts b/src/plugins/files/server/file_client/utils.ts index 88e0901680f0c..b96ca1d87e136 100644 --- a/src/plugins/files/server/file_client/utils.ts +++ b/src/plugins/files/server/file_client/utils.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { errors } from '@elastic/elasticsearch'; import type { FileMetadata } from '../../common'; export function createDefaultFileAttributes(): Pick< @@ -19,3 +20,46 @@ export function createDefaultFileAttributes(): Pick< Updated: dateString, }; } + +export class FilesPluginError extends Error { + constructor(message: string, public readonly meta?: any) { + super(message); + // For debugging - capture name of subclasses + this.name = this.constructor.name; + } +} + +interface WrapErrorAndReThrowInterface { + (e: Error, messagePrefix?: string): never; + withMessagePrefix: (messagePrefix: string) => (e: Error) => never; +} + +/** + * A helper method that can be used with Promises to wrap errors encountered with more details + * info. Mainly useful with calls to SO/ES as those errors normally don't include a good stack + * trace that points to where the error occurred. + * @param e + * @param messagePrefix + */ +export const wrapErrorAndReThrow: WrapErrorAndReThrowInterface = ( + e: Error, + messagePrefix: string = '' +): never => { + if (e instanceof FilesPluginError) { + throw e; + } + + let details: string = ''; + + // Create additional details based on known errors + if (e instanceof errors.ResponseError) { + details = `\nRequest: ${e.meta.meta.request.params.method} ${e.meta.meta.request.params.path}`; + } + + throw new FilesPluginError(messagePrefix + e.message + details, e); +}; +wrapErrorAndReThrow.withMessagePrefix = (messagePrefix: string): ((e: Error) => never) => { + return (e: Error) => { + return wrapErrorAndReThrow(e, messagePrefix); + }; +}; diff --git a/src/plugins/files/tsconfig.json b/src/plugins/files/tsconfig.json index 12cdb22ef0f79..08d910f23c5e9 100644 --- a/src/plugins/files/tsconfig.json +++ b/src/plugins/files/tsconfig.json @@ -31,6 +31,7 @@ "@kbn/logging-mocks", "@kbn/core-elasticsearch-server-mocks", "@kbn/core-saved-objects-server-mocks", + "@kbn/logging", ], "exclude": [ "target/**/*", diff --git a/src/plugins/unified_histogram/public/__mocks__/lens_table_adapter.ts b/src/plugins/unified_histogram/public/__mocks__/lens_table_adapter.ts new file mode 100644 index 0000000000000..60e38fecbbfae --- /dev/null +++ b/src/plugins/unified_histogram/public/__mocks__/lens_table_adapter.ts @@ -0,0 +1,40 @@ +/* + * Copyright 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 { Datatable } from '@kbn/expressions-plugin/common'; + +export const lensTablesAdapterMock: Record = { + default: { + columns: [ + { + id: 'col-0-1', + meta: { + dimensionName: 'Slice size', + type: 'number', + }, + name: 'Field 1', + }, + { + id: 'col-0-2', + meta: { + dimensionName: 'Slice', + type: 'number', + }, + name: 'Field 2', + }, + ], + rows: [ + { + 'col-0-1': 0, + 'col-0-2': 0, + 'col-0-3': 0, + 'col-0-4': 0, + }, + ], + type: 'datatable', + }, +}; diff --git a/src/plugins/unified_histogram/public/__mocks__/services.ts b/src/plugins/unified_histogram/public/__mocks__/services.tsx similarity index 92% rename from src/plugins/unified_histogram/public/__mocks__/services.ts rename to src/plugins/unified_histogram/public/__mocks__/services.tsx index 71058be4f634f..6b6e5a7f46864 100644 --- a/src/plugins/unified_histogram/public/__mocks__/services.ts +++ b/src/plugins/unified_histogram/public/__mocks__/services.tsx @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - +import React from 'react'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { expressionsPluginMock } from '@kbn/expressions-plugin/public/mocks'; import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks'; @@ -33,6 +33,7 @@ export const unifiedHistogramServicesMock = { suggestions: jest.fn(() => allSuggestionsMock), }; }), + EditLensConfigPanelApi: jest.fn().mockResolvedValue(Lens Config Panel Component), }, storage: { get: jest.fn(), diff --git a/src/plugins/unified_histogram/public/chart/chart.test.tsx b/src/plugins/unified_histogram/public/chart/chart.test.tsx index a2c5f3b195a21..b32979cc004e5 100644 --- a/src/plugins/unified_histogram/public/chart/chart.test.tsx +++ b/src/plugins/unified_histogram/public/chart/chart.test.tsx @@ -233,6 +233,17 @@ describe('Chart', () => { expect(component.find(SuggestionSelector).exists()).toBeTruthy(); }); + it('should render the edit on the fly button when chart is visible and suggestions exist', async () => { + const component = await mountComponent({ + currentSuggestion: currentSuggestionMock, + allSuggestions: allSuggestionsMock, + isPlainRecord: true, + }); + expect( + component.find('[data-test-subj="unifiedHistogramEditFlyoutVisualization"]').exists() + ).toBeTruthy(); + }); + it('should render the save button when chart is visible and suggestions exist', async () => { const component = await mountComponent({ currentSuggestion: currentSuggestionMock, diff --git a/src/plugins/unified_histogram/public/chart/chart.tsx b/src/plugins/unified_histogram/public/chart/chart.tsx index f99d9b3de1cea..6b112f3578df9 100644 --- a/src/plugins/unified_histogram/public/chart/chart.tsx +++ b/src/plugins/unified_histogram/public/chart/chart.tsx @@ -6,8 +6,7 @@ * Side Public License, v 1. */ -import { ReactElement, useMemo, useState } from 'react'; -import React, { memo } from 'react'; +import React, { ReactElement, useMemo, useState, useEffect, useCallback, memo } from 'react'; import { EuiButtonIcon, EuiContextMenu, @@ -18,6 +17,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import type { Suggestion } from '@kbn/lens-plugin/public'; +import type { Datatable } from '@kbn/expressions-plugin/common'; import { DataView, DataViewField, DataViewType } from '@kbn/data-views-plugin/public'; import type { LensEmbeddableInput } from '@kbn/lens-plugin/public'; import type { AggregateQuery, Filter, Query, TimeRange } from '@kbn/es-query'; @@ -42,6 +42,7 @@ import { useTotalHits } from './hooks/use_total_hits'; import { useRequestParams } from './hooks/use_request_params'; import { useChartStyles } from './hooks/use_chart_styles'; import { useChartActions } from './hooks/use_chart_actions'; +import { useChartConfigPanel } from './hooks/use_chart_config_panel'; import { getLensAttributes } from './utils/get_lens_attributes'; import { useRefetch } from './hooks/use_refetch'; import { useEditVisualization } from './hooks/use_edit_visualization'; @@ -67,6 +68,7 @@ export interface ChartProps { disableTriggers?: LensEmbeddableInput['disableTriggers']; disabledActions?: LensEmbeddableInput['disabledActions']; input$?: UnifiedHistogramInput$; + lensTablesAdapter?: Record; onResetChartHeight?: () => void; onChartHiddenChange?: (chartHidden: boolean) => void; onTimeIntervalChange?: (timeInterval: string) => void; @@ -101,6 +103,7 @@ export function Chart({ disableTriggers, disabledActions, input$: originalInput$, + lensTablesAdapter, onResetChartHeight, onChartHiddenChange, onTimeIntervalChange, @@ -112,6 +115,7 @@ export function Chart({ onBrushEnd, }: ChartProps) { const [isSaveModalVisible, setIsSaveModalVisible] = useState(false); + const [isFlyoutVisible, setIsFlyoutVisible] = useState(false); const { showChartOptionsPopover, chartRef, @@ -190,6 +194,7 @@ export function Chart({ histogramCss, breakdownFieldSelectorGroupCss, breakdownFieldSelectorItemCss, + suggestionsSelectorItemCss, chartToolButtonCss, } = useChartStyles(chartVisible); @@ -215,6 +220,34 @@ export function Chart({ ] ); + const ChartConfigPanel = useChartConfigPanel({ + services, + lensAttributesContext, + dataView, + lensTablesAdapter, + currentSuggestion, + isFlyoutVisible, + setIsFlyoutVisible, + isPlainRecord, + query: originalQuery, + onSuggestionChange, + }); + + const onSuggestionSelectorChange = useCallback( + (s: Suggestion | undefined) => { + onSuggestionChange?.(s); + }, + [onSuggestionChange] + ); + + useEffect(() => { + // close the flyout for dataview mode + // or if no chart is visible + if (!chartVisible && isFlyoutVisible) { + setIsFlyoutVisible(false); + } + }, [chartVisible, isFlyoutVisible]); + const onEditVisualization = useEditVisualization({ services, dataView, @@ -226,6 +259,24 @@ export function Chart({ const canSaveVisualization = chartVisible && currentSuggestion && services.capabilities.dashboard?.showWriteControls; + const renderEditButton = useMemo( + () => ( + setIsFlyoutVisible(true)} + data-test-subj="unifiedHistogramEditFlyoutVisualization" + aria-label={i18n.translate('unifiedHistogram.editVisualizationButton', { + defaultMessage: 'Edit visualization', + })} + disabled={isFlyoutVisible} + /> + ), + [isFlyoutVisible] + ); + + const canEditVisualizationOnTheFly = isPlainRecord && chartVisible; + return ( )} {chartVisible && currentSuggestion && allSuggestions && allSuggestions?.length > 1 && ( - + )} @@ -296,6 +348,21 @@ export function Chart({ )} + {canEditVisualizationOnTheFly && ( + + {!isFlyoutVisible ? ( + + {renderEditButton} + + ) : ( + renderEditButton + )} + + )} {onEditVisualization && ( )} + {isFlyoutVisible && ChartConfigPanel} ); } diff --git a/src/plugins/unified_histogram/public/chart/histogram.tsx b/src/plugins/unified_histogram/public/chart/histogram.tsx index 61320c627bb13..63a0c9897b8c7 100644 --- a/src/plugins/unified_histogram/public/chart/histogram.tsx +++ b/src/plugins/unified_histogram/public/chart/histogram.tsx @@ -146,6 +146,7 @@ export function Histogram({ const chartCss = css` position: relative; flex-grow: 1; + margin-block: ${euiTheme.size.xs}; & > div { height: 100%; diff --git a/src/plugins/unified_histogram/public/chart/hooks/use_chart_config_panel.test.tsx b/src/plugins/unified_histogram/public/chart/hooks/use_chart_config_panel.test.tsx new file mode 100644 index 0000000000000..5f0d4b17e80db --- /dev/null +++ b/src/plugins/unified_histogram/public/chart/hooks/use_chart_config_panel.test.tsx @@ -0,0 +1,66 @@ +/* + * Copyright 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 { TypedLensByValueInput } from '@kbn/lens-plugin/public'; +import { renderHook } from '@testing-library/react-hooks'; +import { act } from 'react-test-renderer'; +import { setTimeout } from 'timers/promises'; +import { dataViewWithTimefieldMock } from '../../__mocks__/data_view_with_timefield'; +import { unifiedHistogramServicesMock } from '../../__mocks__/services'; +import { lensTablesAdapterMock } from '../../__mocks__/lens_table_adapter'; +import { useChartConfigPanel } from './use_chart_config_panel'; +import type { LensAttributesContext } from '../utils/get_lens_attributes'; + +describe('useChartConfigPanel', () => { + it('should return a jsx element to edit the visualization', async () => { + const lensAttributes = { + visualizationType: 'lnsXY', + title: 'test', + } as TypedLensByValueInput['attributes']; + const hook = renderHook(() => + useChartConfigPanel({ + services: unifiedHistogramServicesMock, + dataView: dataViewWithTimefieldMock, + lensAttributesContext: { + attributes: lensAttributes, + } as unknown as LensAttributesContext, + isFlyoutVisible: true, + setIsFlyoutVisible: jest.fn(), + isPlainRecord: true, + lensTablesAdapter: lensTablesAdapterMock, + query: { + sql: 'Select * from test', + }, + }) + ); + await act(() => setTimeout(0)); + expect(hook.result.current).toBeDefined(); + expect(hook.result.current).not.toBeNull(); + }); + + it('should return null if not in text based mode', async () => { + const lensAttributes = { + visualizationType: 'lnsXY', + title: 'test', + } as TypedLensByValueInput['attributes']; + const hook = renderHook(() => + useChartConfigPanel({ + services: unifiedHistogramServicesMock, + dataView: dataViewWithTimefieldMock, + lensAttributesContext: { + attributes: lensAttributes, + } as unknown as LensAttributesContext, + isFlyoutVisible: true, + setIsFlyoutVisible: jest.fn(), + isPlainRecord: false, + }) + ); + await act(() => setTimeout(0)); + expect(hook.result.current).toBeNull(); + }); +}); diff --git a/src/plugins/unified_histogram/public/chart/hooks/use_chart_config_panel.tsx b/src/plugins/unified_histogram/public/chart/hooks/use_chart_config_panel.tsx new file mode 100644 index 0000000000000..99fb8fbb8fe31 --- /dev/null +++ b/src/plugins/unified_histogram/public/chart/hooks/use_chart_config_panel.tsx @@ -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 React, { useCallback, useEffect, useRef, useState } from 'react'; +import type { AggregateQuery, Query } from '@kbn/es-query'; +import { isEqual } from 'lodash'; +import type { Suggestion } from '@kbn/lens-plugin/public'; +import type { DataView } from '@kbn/data-views-plugin/public'; +import type { Datatable } from '@kbn/expressions-plugin/common'; + +import type { UnifiedHistogramServices } from '../../types'; +import type { LensAttributesContext } from '../utils/get_lens_attributes'; + +export function useChartConfigPanel({ + services, + lensAttributesContext, + dataView, + lensTablesAdapter, + currentSuggestion, + isFlyoutVisible, + setIsFlyoutVisible, + isPlainRecord, + query, + onSuggestionChange, +}: { + services: UnifiedHistogramServices; + lensAttributesContext: LensAttributesContext; + dataView: DataView; + isFlyoutVisible: boolean; + setIsFlyoutVisible: (flag: boolean) => void; + lensTablesAdapter?: Record; + currentSuggestion?: Suggestion; + isPlainRecord?: boolean; + query?: Query | AggregateQuery; + onSuggestionChange?: (suggestion: Suggestion | undefined) => void; +}) { + const [editLensConfigPanel, setEditLensConfigPanel] = useState(null); + const previousSuggestion = useRef(undefined); + const previousAdapters = useRef | undefined>(undefined); + const previousQuery = useRef(undefined); + const updateSuggestion = useCallback( + (datasourceState, visualizationState) => { + const updatedSuggestion = { + ...currentSuggestion, + ...(datasourceState && { datasourceState }), + ...(visualizationState && { visualizationState }), + } as Suggestion; + onSuggestionChange?.(updatedSuggestion); + }, + [currentSuggestion, onSuggestionChange] + ); + + useEffect(() => { + const dataHasChanged = + Boolean(lensTablesAdapter) && + !isEqual(previousAdapters.current, lensTablesAdapter) && + query !== previousQuery?.current; + async function fetchLensConfigComponent() { + const Component = await services.lens.EditLensConfigPanelApi(); + const panel = ( + + ); + setEditLensConfigPanel(panel); + previousSuggestion.current = currentSuggestion; + previousAdapters.current = lensTablesAdapter; + if (dataHasChanged) { + previousQuery.current = query; + } + } + const suggestionHasChanged = currentSuggestion?.title !== previousSuggestion?.current?.title; + // rerender the component if the data has changed or the suggestion + // as I can have different suggestions for the same data + if (isPlainRecord && (dataHasChanged || suggestionHasChanged || !isFlyoutVisible)) { + fetchLensConfigComponent(); + } + }, [ + lensAttributesContext.attributes, + services.lens, + dataView, + updateSuggestion, + isPlainRecord, + currentSuggestion, + query, + isFlyoutVisible, + lensTablesAdapter, + setIsFlyoutVisible, + ]); + + return isPlainRecord ? editLensConfigPanel : null; +} diff --git a/src/plugins/unified_histogram/public/chart/hooks/use_chart_styles.tsx b/src/plugins/unified_histogram/public/chart/hooks/use_chart_styles.tsx index c019c7cef981e..13b527be702c1 100644 --- a/src/plugins/unified_histogram/public/chart/hooks/use_chart_styles.tsx +++ b/src/plugins/unified_histogram/public/chart/hooks/use_chart_styles.tsx @@ -56,6 +56,11 @@ export const useChartStyles = (chartVisible: boolean) => { align-items: flex-end; padding-left: ${euiTheme.size.s}; `; + const suggestionsSelectorItemCss = css` + min-width: 0; + align-items: flex-start; + padding-left: ${euiTheme.size.s}; + `; const chartToolButtonCss = css` display: flex; justify-content: center; @@ -70,6 +75,7 @@ export const useChartStyles = (chartVisible: boolean) => { histogramCss, breakdownFieldSelectorGroupCss, breakdownFieldSelectorItemCss, + suggestionsSelectorItemCss, chartToolButtonCss, }; }; diff --git a/src/plugins/unified_histogram/public/chart/hooks/use_edit_visualization.test.ts b/src/plugins/unified_histogram/public/chart/hooks/use_edit_visualization.test.ts index 8bd9e9161c837..ec213dd53e141 100644 --- a/src/plugins/unified_histogram/public/chart/hooks/use_edit_visualization.test.ts +++ b/src/plugins/unified_histogram/public/chart/hooks/use_edit_visualization.test.ts @@ -81,6 +81,21 @@ describe('useEditVisualization', () => { expect(hook.result.current).toBeUndefined(); }); + it('should return undefined if is on text based mode', async () => { + getTriggerCompatibleActions.mockReturnValue(Promise.resolve([{ id: 'test' }])); + const hook = renderHook(() => + useEditVisualization({ + services: unifiedHistogramServicesMock, + dataView: dataViewWithTimefieldMock, + relativeTimeRange: { from: 'now-15m', to: 'now' }, + lensAttributes: {} as unknown as TypedLensByValueInput['attributes'], + isPlainRecord: true, + }) + ); + await act(() => setTimeout(0)); + expect(hook.result.current).toBeUndefined(); + }); + it('should return undefined if the time field is not visualizable', async () => { getTriggerCompatibleActions.mockReturnValue(Promise.resolve([{ id: 'test' }])); const dataView = { diff --git a/src/plugins/unified_histogram/public/chart/hooks/use_edit_visualization.ts b/src/plugins/unified_histogram/public/chart/hooks/use_edit_visualization.ts index e681fd34cd91e..b02732bfcbfc9 100644 --- a/src/plugins/unified_histogram/public/chart/hooks/use_edit_visualization.ts +++ b/src/plugins/unified_histogram/public/chart/hooks/use_edit_visualization.ts @@ -32,10 +32,10 @@ export const useEditVisualization = ({ const [canVisualize, setCanVisualize] = useState(false); const checkCanVisualize = useCallback(async () => { - if (!dataView.id) { + if (!dataView.id || isPlainRecord) { return false; } - if (!isPlainRecord && (!dataView.isTimeBased() || !dataView.getTimeField().visualizable)) { + if (!dataView.isTimeBased() || !dataView.getTimeField().visualizable) { return false; } diff --git a/src/plugins/unified_histogram/public/chart/suggestion_selector.tsx b/src/plugins/unified_histogram/public/chart/suggestion_selector.tsx index 85860cf2c9bc8..0196387633396 100644 --- a/src/plugins/unified_histogram/public/chart/suggestion_selector.tsx +++ b/src/plugins/unified_histogram/public/chart/suggestion_selector.tsx @@ -67,7 +67,7 @@ export const SuggestionSelector = ({ const { euiTheme } = useEuiTheme(); const suggestionComboCss = css` width: 100%; - max-width: ${euiTheme.base * 22}px; + max-width: ${euiTheme.base * 15}px; `; return ( @@ -78,9 +78,7 @@ export const SuggestionSelector = ({ > } placeholder={i18n.translate('unifiedHistogram.suggestionSelectorPlaceholder', { defaultMessage: 'Select visualization', })} @@ -88,9 +86,9 @@ export const SuggestionSelector = ({ options={suggestionOptions} selectedOptions={selectedSuggestion} onChange={onSelectionChange} - compressed fullWidth={true} isClearable={false} + compressed onFocus={disableFieldPopover} onBlur={enableFieldPopover} renderOption={(option) => { diff --git a/src/plugins/unified_histogram/public/container/hooks/use_state_props.test.ts b/src/plugins/unified_histogram/public/container/hooks/use_state_props.test.ts index fcdd194410db0..c0eeb9448eee7 100644 --- a/src/plugins/unified_histogram/public/container/hooks/use_state_props.test.ts +++ b/src/plugins/unified_histogram/public/container/hooks/use_state_props.test.ts @@ -15,6 +15,7 @@ import { UnifiedHistogramFetchStatus } from '../../types'; import { dataViewMock } from '../../__mocks__/data_view'; import { dataViewWithTimefieldMock } from '../../__mocks__/data_view_with_timefield'; import { currentSuggestionMock } from '../../__mocks__/suggestions'; +import { lensTablesAdapterMock } from '../../__mocks__/lens_table_adapter'; import { unifiedHistogramServicesMock } from '../../__mocks__/services'; import { createStateService, @@ -28,6 +29,7 @@ describe('useStateProps', () => { breakdownField: 'bytes', chartHidden: false, lensRequestAdapter: new RequestAdapter(), + lensTablesAdapter: lensTablesAdapterMock, timeInterval: 'auto', topPanelHeight: 100, totalHitsStatus: UnifiedHistogramFetchStatus.uninitialized, @@ -82,6 +84,37 @@ describe('useStateProps', () => { "total": undefined, }, "isPlainRecord": false, + "lensTablesAdapter": Object { + "default": Object { + "columns": Array [ + Object { + "id": "col-0-1", + "meta": Object { + "dimensionName": "Slice size", + "type": "number", + }, + "name": "Field 1", + }, + Object { + "id": "col-0-2", + "meta": Object { + "dimensionName": "Slice", + "type": "number", + }, + "name": "Field 2", + }, + ], + "rows": Array [ + Object { + "col-0-1": 0, + "col-0-2": 0, + "col-0-3": 0, + "col-0-4": 0, + }, + ], + "type": "datatable", + }, + }, "onBreakdownFieldChange": [Function], "onChartHiddenChange": [Function], "onChartLoad": [Function], @@ -126,6 +159,37 @@ describe('useStateProps', () => { "total": undefined, }, "isPlainRecord": true, + "lensTablesAdapter": Object { + "default": Object { + "columns": Array [ + Object { + "id": "col-0-1", + "meta": Object { + "dimensionName": "Slice size", + "type": "number", + }, + "name": "Field 1", + }, + Object { + "id": "col-0-2", + "meta": Object { + "dimensionName": "Slice", + "type": "number", + }, + "name": "Field 2", + }, + ], + "rows": Array [ + Object { + "col-0-1": 0, + "col-0-2": 0, + "col-0-3": 0, + "col-0-4": 0, + }, + ], + "type": "datatable", + }, + }, "onBreakdownFieldChange": [Function], "onChartHiddenChange": [Function], "onChartLoad": [Function], @@ -191,6 +255,37 @@ describe('useStateProps', () => { "total": undefined, }, "isPlainRecord": false, + "lensTablesAdapter": Object { + "default": Object { + "columns": Array [ + Object { + "id": "col-0-1", + "meta": Object { + "dimensionName": "Slice size", + "type": "number", + }, + "name": "Field 1", + }, + Object { + "id": "col-0-2", + "meta": Object { + "dimensionName": "Slice", + "type": "number", + }, + "name": "Field 2", + }, + ], + "rows": Array [ + Object { + "col-0-1": 0, + "col-0-2": 0, + "col-0-3": 0, + "col-0-4": 0, + }, + ], + "type": "datatable", + }, + }, "onBreakdownFieldChange": [Function], "onChartHiddenChange": [Function], "onChartLoad": [Function], @@ -232,6 +327,37 @@ describe('useStateProps', () => { "total": undefined, }, "isPlainRecord": false, + "lensTablesAdapter": Object { + "default": Object { + "columns": Array [ + Object { + "id": "col-0-1", + "meta": Object { + "dimensionName": "Slice size", + "type": "number", + }, + "name": "Field 1", + }, + Object { + "id": "col-0-2", + "meta": Object { + "dimensionName": "Slice", + "type": "number", + }, + "name": "Field 2", + }, + ], + "rows": Array [ + Object { + "col-0-1": 0, + "col-0-2": 0, + "col-0-3": 0, + "col-0-4": 0, + }, + ], + "type": "datatable", + }, + }, "onBreakdownFieldChange": [Function], "onChartHiddenChange": [Function], "onChartLoad": [Function], diff --git a/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts b/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts index b9c4570cdecb9..a5845731cf12e 100644 --- a/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts +++ b/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts @@ -23,6 +23,7 @@ import { timeIntervalSelector, totalHitsResultSelector, totalHitsStatusSelector, + lensTablesAdapterSelector, } from '../utils/state_selectors'; import { useStateSelector } from '../utils/use_state_selector'; @@ -44,7 +45,7 @@ export const useStateProps = ({ const timeInterval = useStateSelector(stateService?.state$, timeIntervalSelector); const totalHitsResult = useStateSelector(stateService?.state$, totalHitsResultSelector); const totalHitsStatus = useStateSelector(stateService?.state$, totalHitsStatusSelector); - + const lensTablesAdapter = useStateSelector(stateService?.state$, lensTablesAdapterSelector); /** * Contexts */ @@ -139,6 +140,7 @@ export const useStateProps = ({ (event: UnifiedHistogramChartLoadEvent) => { // We need to store the Lens request adapter in order to inspect its requests stateService?.setLensRequestAdapter(event.adapters.requests); + stateService?.setLensTablesAdapter(event.adapters.tables?.tables); }, [stateService] ); @@ -174,6 +176,7 @@ export const useStateProps = ({ breakdown, request, isPlainRecord, + lensTablesAdapter, onTopPanelHeightChange, onTimeIntervalChange, onTotalHitsChange, diff --git a/src/plugins/unified_histogram/public/container/services/state_service.test.ts b/src/plugins/unified_histogram/public/container/services/state_service.test.ts index eb839e6eaba0f..eb7232e889037 100644 --- a/src/plugins/unified_histogram/public/container/services/state_service.test.ts +++ b/src/plugins/unified_histogram/public/container/services/state_service.test.ts @@ -9,6 +9,7 @@ import { RequestAdapter } from '@kbn/inspector-plugin/common'; import { UnifiedHistogramFetchStatus } from '../..'; import { unifiedHistogramServicesMock } from '../../__mocks__/services'; +import { lensTablesAdapterMock } from '../../__mocks__/lens_table_adapter'; import { getChartHidden, getTopPanelHeight, @@ -46,6 +47,7 @@ describe('UnifiedHistogramStateService', () => { breakdownField: 'bytes', chartHidden: false, lensRequestAdapter: new RequestAdapter(), + lensTablesAdapter: lensTablesAdapterMock, timeInterval: 'auto', topPanelHeight: 100, totalHitsStatus: UnifiedHistogramFetchStatus.uninitialized, @@ -134,6 +136,8 @@ describe('UnifiedHistogramStateService', () => { expect(state).toEqual(newState); stateService.setLensRequestAdapter(undefined); newState = { ...newState, lensRequestAdapter: undefined }; + stateService.setLensTablesAdapter(undefined); + newState = { ...newState, lensTablesAdapter: undefined }; expect(state).toEqual(newState); stateService.setTotalHits({ totalHitsStatus: UnifiedHistogramFetchStatus.complete, diff --git a/src/plugins/unified_histogram/public/container/services/state_service.ts b/src/plugins/unified_histogram/public/container/services/state_service.ts index 01d1a7f0c3f60..1fd0905d86c7a 100644 --- a/src/plugins/unified_histogram/public/container/services/state_service.ts +++ b/src/plugins/unified_histogram/public/container/services/state_service.ts @@ -8,6 +8,7 @@ import type { RequestAdapter } from '@kbn/inspector-plugin/common'; import type { Suggestion } from '@kbn/lens-plugin/public'; +import type { Datatable } from '@kbn/expressions-plugin/common'; import { BehaviorSubject, Observable } from 'rxjs'; import { UnifiedHistogramFetchStatus } from '../..'; import type { UnifiedHistogramServices } from '../../types'; @@ -40,6 +41,10 @@ export interface UnifiedHistogramState { * The current Lens request adapter */ lensRequestAdapter: RequestAdapter | undefined; + /** + * The current Lens request table + */ + lensTablesAdapter?: Record; /** * The current time interval of the chart */ @@ -108,6 +113,10 @@ export interface UnifiedHistogramStateService { * Sets the current Lens request adapter */ setLensRequestAdapter: (lensRequestAdapter: RequestAdapter | undefined) => void; + /** + * Sets the current Lens tables + */ + setLensTablesAdapter: (lensTablesAdapter: Record | undefined) => void; /** * Sets the current total hits status and result */ @@ -190,6 +199,10 @@ export const createStateService = ( updateState({ lensRequestAdapter }); }, + setLensTablesAdapter: (lensTablesAdapter: Record | undefined) => { + updateState({ lensTablesAdapter }); + }, + setTotalHits: (totalHits: { totalHitsStatus: UnifiedHistogramFetchStatus; totalHitsResult: number | Error | undefined; diff --git a/src/plugins/unified_histogram/public/container/utils/state_selectors.ts b/src/plugins/unified_histogram/public/container/utils/state_selectors.ts index d11fb1182cc45..80e809f4fc38f 100644 --- a/src/plugins/unified_histogram/public/container/utils/state_selectors.ts +++ b/src/plugins/unified_histogram/public/container/utils/state_selectors.ts @@ -15,3 +15,4 @@ export const topPanelHeightSelector = (state: UnifiedHistogramState) => state.to export const totalHitsResultSelector = (state: UnifiedHistogramState) => state.totalHitsResult; export const totalHitsStatusSelector = (state: UnifiedHistogramState) => state.totalHitsStatus; export const currentSuggestionSelector = (state: UnifiedHistogramState) => state.currentSuggestion; +export const lensTablesAdapterSelector = (state: UnifiedHistogramState) => state.lensTablesAdapter; diff --git a/src/plugins/unified_histogram/public/layout/layout.tsx b/src/plugins/unified_histogram/public/layout/layout.tsx index 3c5b021e0b08d..cd6d2ffb5ec07 100644 --- a/src/plugins/unified_histogram/public/layout/layout.tsx +++ b/src/plugins/unified_histogram/public/layout/layout.tsx @@ -11,6 +11,7 @@ import { PropsWithChildren, ReactElement, RefObject } from 'react'; import React, { useMemo } from 'react'; import { createHtmlPortalNode, InPortal, OutPortal } from 'react-reverse-portal'; import { css } from '@emotion/css'; +import type { Datatable } from '@kbn/expressions-plugin/common'; import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; import type { LensEmbeddableInput, LensSuggestionsApi, Suggestion } from '@kbn/lens-plugin/public'; import { AggregateQuery, Filter, Query, TimeRange } from '@kbn/es-query'; @@ -77,6 +78,7 @@ export interface UnifiedHistogramLayoutProps extends PropsWithChildren * Context object for the hits count -- leave undefined to hide the hits count */ hits?: UnifiedHistogramHitsContext; + lensTablesAdapter?: Record; /** * Context object for the chart -- leave undefined to hide the chart */ @@ -169,6 +171,7 @@ export const UnifiedHistogramLayout = ({ columns, request, hits, + lensTablesAdapter, chart: originalChart, breakdown, resizeRef, @@ -273,6 +276,7 @@ export const UnifiedHistogramLayout = ({ onChartLoad={onChartLoad} onFilter={onFilter} onBrushEnd={onBrushEnd} + lensTablesAdapter={lensTablesAdapter} /> {children} diff --git a/test/accessibility/apps/dashboard.ts b/test/accessibility/apps/dashboard.ts index 8c2924a0901c1..32d198c7d5e02 100644 --- a/test/accessibility/apps/dashboard.ts +++ b/test/accessibility/apps/dashboard.ts @@ -18,7 +18,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('Dashboard', () => { const dashboardName = 'Dashboard Listing A11y'; - const clonedDashboardName = 'Dashboard Listing A11y Copy'; + const clonedDashboardName = 'Dashboard Listing A11y (1)'; it('dashboard', async () => { await PageObjects.common.navigateToApp('dashboard'); @@ -132,11 +132,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await a11y.testAppSnapshot(); }); - it('Confirm clone with *copy* appended', async () => { - await PageObjects.dashboard.confirmClone(); - await a11y.testAppSnapshot(); - }); - it('Dashboard listing table', async () => { await PageObjects.dashboard.gotoDashboardLandingPage(); await a11y.testAppSnapshot(); diff --git a/test/functional/apps/dashboard/group4/dashboard_clone.ts b/test/functional/apps/dashboard/group4/dashboard_clone.ts index 26738760b28e4..c62c1a4195900 100644 --- a/test/functional/apps/dashboard/group4/dashboard_clone.ts +++ b/test/functional/apps/dashboard/group4/dashboard_clone.ts @@ -17,7 +17,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('dashboard clone', function describeIndexTests() { const dashboardName = 'Dashboard Clone Test'; - const clonedDashboardName = dashboardName + ' Copy'; + const clonedDashboardName = dashboardName + ' (1)'; before(async function () { return PageObjects.dashboard.initTests(); @@ -31,7 +31,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.dashboard.saveDashboard(dashboardName); await PageObjects.dashboard.clickClone(); - await PageObjects.dashboard.confirmClone(); await PageObjects.dashboard.gotoDashboardLandingPage(); await listingTable.searchAndExpectItemsCount('dashboard', clonedDashboardName, 1); }); @@ -43,38 +42,5 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(panelTitles).to.eql(PageObjects.dashboard.getTestVisualizationNames()); }); }); - - it('clone appends Copy to the dashboard title name', async () => { - await PageObjects.dashboard.loadSavedDashboard(dashboardName); - await PageObjects.dashboard.clickClone(); - - const title = await PageObjects.dashboard.getCloneTitle(); - expect(title).to.be(clonedDashboardName); - }); - - it('and warns on duplicate name', async function () { - await PageObjects.dashboard.confirmClone(); - await PageObjects.dashboard.expectDuplicateTitleWarningDisplayed({ displayed: true }); - }); - - it("and doesn't save", async () => { - await PageObjects.dashboard.cancelClone(); - await PageObjects.dashboard.gotoDashboardLandingPage(); - - await listingTable.searchAndExpectItemsCount('dashboard', dashboardName, 1); - }); - - it('Clones on confirm duplicate title warning', async function () { - await PageObjects.dashboard.loadSavedDashboard(dashboardName); - await PageObjects.dashboard.clickClone(); - - await PageObjects.dashboard.confirmClone(); - await PageObjects.dashboard.expectDuplicateTitleWarningDisplayed({ displayed: true }); - await PageObjects.dashboard.confirmClone(); - await PageObjects.dashboard.waitForRenderComplete(); - await PageObjects.dashboard.gotoDashboardLandingPage(); - - await listingTable.searchAndExpectItemsCount('dashboard', dashboardName + ' Copy', 2); - }); }); } diff --git a/test/functional/page_objects/dashboard_page.ts b/test/functional/page_objects/dashboard_page.ts index 602f0f9d3fd92..897ceb5564d93 100644 --- a/test/functional/page_objects/dashboard_page.ts +++ b/test/functional/page_objects/dashboard_page.ts @@ -212,24 +212,6 @@ export class DashboardPageObject extends FtrService { await this.testSubjects.click('dashboardClone'); } - public async getCloneTitle() { - return await this.testSubjects.getAttribute('clonedDashboardTitle', 'value'); - } - - public async confirmClone() { - this.log.debug('Confirming clone'); - await this.testSubjects.click('cloneConfirmButton'); - } - - public async cancelClone() { - this.log.debug('Canceling clone'); - await this.testSubjects.click('cloneCancelButton'); - } - - public async setClonedDashboardTitle(title: string) { - await this.testSubjects.setValue('clonedDashboardTitle', title); - } - /** * Asserts that the duplicate title warning is either displayed or not displayed. * @param { displayed: boolean } diff --git a/test/plugin_functional/test_suites/core_plugins/rendering.ts b/test/plugin_functional/test_suites/core_plugins/rendering.ts index b6b53ce106c8c..c4f5cf5a69568 100644 --- a/test/plugin_functional/test_suites/core_plugins/rendering.ts +++ b/test/plugin_functional/test_suites/core_plugins/rendering.ts @@ -215,6 +215,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) { 'xpack.cloud_integrations.gain_sight.org_id (any)', 'xpack.cloud.id (string)', 'xpack.cloud.organization_url (string)', + 'xpack.cloud.billing_url (string)', 'xpack.cloud.profile_url (string)', 'xpack.discoverEnhanced.actions.exploreDataInChart.enabled (boolean)', 'xpack.discoverEnhanced.actions.exploreDataInContextMenu.enabled (boolean)', diff --git a/tsconfig.base.json b/tsconfig.base.json index b1a515ff31d50..6db4864f58f05 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1098,6 +1098,8 @@ "@kbn/rollup-plugin/*": ["x-pack/plugins/rollup/*"], "@kbn/routing-example-plugin": ["examples/routing_example"], "@kbn/routing-example-plugin/*": ["examples/routing_example/*"], + "@kbn/rrule": ["packages/kbn-rrule"], + "@kbn/rrule/*": ["packages/kbn-rrule/*"], "@kbn/rule-data-utils": ["packages/kbn-rule-data-utils"], "@kbn/rule-data-utils/*": ["packages/kbn-rule-data-utils/*"], "@kbn/rule-registry-plugin": ["x-pack/plugins/rule_registry"], @@ -1478,6 +1480,8 @@ "@kbn/utils/*": ["packages/kbn-utils/*"], "@kbn/ux-plugin": ["x-pack/plugins/ux"], "@kbn/ux-plugin/*": ["x-pack/plugins/ux/*"], + "@kbn/v8-profiler-examples-plugin": ["examples/v8_profiler_examples"], + "@kbn/v8-profiler-examples-plugin/*": ["examples/v8_profiler_examples/*"], "@kbn/validate-next-docs-cli": ["packages/kbn-validate-next-docs-cli"], "@kbn/validate-next-docs-cli/*": ["packages/kbn-validate-next-docs-cli/*"], "@kbn/vis-default-editor-plugin": ["src/plugins/vis_default_editor"], diff --git a/x-pack/packages/security-solution/data_table/components/data_table/index.tsx b/x-pack/packages/security-solution/data_table/components/data_table/index.tsx index b11c932b60b58..6c71d796dc9ea 100644 --- a/x-pack/packages/security-solution/data_table/components/data_table/index.tsx +++ b/x-pack/packages/security-solution/data_table/components/data_table/index.tsx @@ -338,7 +338,7 @@ export const DataTableComponent = React.memo( ? // TODO use FieldSpec object instead of column columnHeaders.map((column) => ({ name: column.id, - type: column.type ?? 'keyword', + type: column.type ?? '', // When type is an empty string all cell actions are incompatible aggregatable: column.aggregatable ?? false, searchable: column.searchable ?? false, esTypes: column.esTypes ?? [], diff --git a/x-pack/performance/journeys/cloud_security_dashboard.ts b/x-pack/performance/journeys/cloud_security_dashboard.ts index 39625126e7067..a7256f44717f2 100644 --- a/x-pack/performance/journeys/cloud_security_dashboard.ts +++ b/x-pack/performance/journeys/cloud_security_dashboard.ts @@ -14,6 +14,7 @@ export const journey = new Journey({ const response = await kibanaServer.request({ path: '/internal/cloud_security_posture/status?check=init', method: 'GET', + headers: { 'elastic-api-version': '1' }, }); expect(response.status).to.eql(200); expect(response.data).to.eql({ isPluginInitialized: true }); diff --git a/x-pack/plugins/alerting/common/rrule_type.ts b/x-pack/plugins/alerting/common/rrule_type.ts index 2405e2b25b7eb..7d250a0302317 100644 --- a/x-pack/plugins/alerting/common/rrule_type.ts +++ b/x-pack/plugins/alerting/common/rrule_type.ts @@ -5,26 +5,14 @@ * 2.0. */ -import type { WeekdayStr } from 'rrule'; +import type { WeekdayStr, Options } from '@kbn/rrule'; export type RRuleParams = Partial & Pick; // An iCal RRULE to define a recurrence schedule, see https://github.com/jakubroztocil/rrule for the spec -export interface RRuleRecord { +export type RRuleRecord = Omit & { dtstart: string; - tzid: string; - freq?: 0 | 1 | 2 | 3 | 4 | 5 | 6; - until?: string; - count?: number; - interval?: number; + byweekday?: Array; wkst?: WeekdayStr; - byweekday?: Array; - bymonth?: number[]; - bysetpos?: number[]; - bymonthday: number[]; - byyearday: number[]; - byweekno: number[]; - byhour: number[]; - byminute: number[]; - bysecond: number[]; -} + until?: string; +}; diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/recurring_schedule_form/custom_recurring_schedule.test.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/recurring_schedule_form/custom_recurring_schedule.test.tsx index 67f2af2d5df2c..92e792e4523e4 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/recurring_schedule_form/custom_recurring_schedule.test.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/recurring_schedule_form/custom_recurring_schedule.test.tsx @@ -8,10 +8,11 @@ import React from 'react'; import { fireEvent, waitFor, within } from '@testing-library/react'; import { useForm, Form } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { Frequency } from '@kbn/rrule'; import { AppMockRenderer, createAppMockRenderer } from '../../../../lib/test_utils'; import { FormProps, schema } from '../schema'; import { CustomRecurringSchedule } from './custom_recurring_schedule'; -import { EndsOptions, Frequency } from '../../constants'; +import { EndsOptions } from '../../constants'; const initialValue: FormProps = { title: 'test', diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/recurring_schedule_form/custom_recurring_schedule.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/recurring_schedule_form/custom_recurring_schedule.tsx index 939896271bbac..749b5b8b5be30 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/recurring_schedule_form/custom_recurring_schedule.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/recurring_schedule_form/custom_recurring_schedule.tsx @@ -5,6 +5,7 @@ * 2.0. */ import React, { useMemo } from 'react'; +import { Frequency } from '@kbn/rrule'; import moment from 'moment'; import { css } from '@emotion/react'; import { @@ -17,7 +18,7 @@ import { MultiButtonGroupFieldValue, } from '@kbn/es-ui-shared-plugin/static/forms/components'; import { EuiFlexGroup, EuiFlexItem, EuiFormLabel, EuiSpacer } from '@elastic/eui'; -import { CREATE_FORM_CUSTOM_FREQUENCY, Frequency, WEEKDAY_OPTIONS } from '../../constants'; +import { CREATE_FORM_CUSTOM_FREQUENCY, WEEKDAY_OPTIONS } from '../../constants'; import * as i18n from '../../translations'; import { getInitialByWeekday } from '../../helpers/get_initial_by_weekday'; import { getWeekdayInfo } from '../../helpers/get_weekday_info'; @@ -105,7 +106,7 @@ export const CustomRecurringSchedule: React.FC = React.memo(() => { ) : null} - {recurringSchedule?.customFrequency === Frequency.WEEKLY || + {Number(recurringSchedule?.customFrequency) === Frequency.WEEKLY || recurringSchedule?.frequency === Frequency.DAILY ? ( { /> ) : null} - {recurringSchedule?.customFrequency === Frequency.MONTHLY ? ( + {Number(recurringSchedule?.customFrequency) === Frequency.MONTHLY ? ( ; bymonth?: string; } diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/constants.ts b/x-pack/plugins/alerting/public/pages/maintenance_windows/constants.ts index 36fb85cff6b0f..22b992e3e6382 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/constants.ts +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/constants.ts @@ -5,17 +5,15 @@ * 2.0. */ import { invert, mapValues } from 'lodash'; +import { Frequency } from '@kbn/rrule'; import moment from 'moment'; import * as i18n from './translations'; import { ISO_WEEKDAYS, MaintenanceWindowStatus } from '../../../common'; -// TODO - consolidate enum with backend -export enum Frequency { - YEARLY = '0', - MONTHLY = '1', - WEEKLY = '2', - DAILY = '3', -} +export type MaintenanceWindowFrequency = Extract< + Frequency, + Frequency.YEARLY | Frequency.MONTHLY | Frequency.WEEKLY | Frequency.DAILY +>; export const DEFAULT_FREQUENCY_OPTIONS = [ { diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_from_maintenance_window_to_form.test.ts b/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_from_maintenance_window_to_form.test.ts index c892f7db66729..eb187823a685b 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_from_maintenance_window_to_form.test.ts +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_from_maintenance_window_to_form.test.ts @@ -7,8 +7,7 @@ import moment from 'moment'; -import { Frequency } from '../constants'; -import { RRuleFrequency } from '../types'; +import { Frequency } from '@kbn/rrule'; import { convertFromMaintenanceWindowToForm } from './convert_from_maintenance_window_to_form'; describe('convertFromMaintenanceWindowToForm', () => { @@ -25,7 +24,7 @@ describe('convertFromMaintenanceWindowToForm', () => { rRule: { dtstart: startDate.toISOString(), tzid: 'UTC', - freq: RRuleFrequency.YEARLY, + freq: Frequency.YEARLY, count: 1, }, }); @@ -46,7 +45,7 @@ describe('convertFromMaintenanceWindowToForm', () => { rRule: { dtstart: startDate.toISOString(), tzid: 'UTC', - freq: RRuleFrequency.DAILY, + freq: Frequency.DAILY, interval: 1, byweekday: ['WE'], }, @@ -76,7 +75,7 @@ describe('convertFromMaintenanceWindowToForm', () => { rRule: { dtstart: startDate.toISOString(), tzid: 'UTC', - freq: RRuleFrequency.DAILY, + freq: Frequency.DAILY, interval: 1, byweekday: ['WE'], until, @@ -106,7 +105,7 @@ describe('convertFromMaintenanceWindowToForm', () => { rRule: { dtstart: startDate.toISOString(), tzid: 'UTC', - freq: RRuleFrequency.DAILY, + freq: Frequency.DAILY, interval: 1, byweekday: ['WE'], count: 3, @@ -136,7 +135,7 @@ describe('convertFromMaintenanceWindowToForm', () => { rRule: { dtstart: startDate.toISOString(), tzid: 'UTC', - freq: RRuleFrequency.WEEKLY, + freq: Frequency.WEEKLY, interval: 1, byweekday: ['WE'], }, @@ -164,7 +163,7 @@ describe('convertFromMaintenanceWindowToForm', () => { rRule: { dtstart: startDate.toISOString(), tzid: 'UTC', - freq: RRuleFrequency.MONTHLY, + freq: Frequency.MONTHLY, interval: 1, byweekday: ['+4WE'], }, @@ -192,7 +191,7 @@ describe('convertFromMaintenanceWindowToForm', () => { rRule: { dtstart: startDate.toISOString(), tzid: 'UTC', - freq: RRuleFrequency.YEARLY, + freq: Frequency.YEARLY, interval: 1, bymonth: [3], bymonthday: [22], @@ -220,7 +219,7 @@ describe('convertFromMaintenanceWindowToForm', () => { rRule: { dtstart: startDate.toISOString(), tzid: 'UTC', - freq: RRuleFrequency.DAILY, + freq: Frequency.DAILY, interval: 1, }, }); @@ -247,7 +246,7 @@ describe('convertFromMaintenanceWindowToForm', () => { rRule: { dtstart: startDate.toISOString(), tzid: 'UTC', - freq: RRuleFrequency.WEEKLY, + freq: Frequency.WEEKLY, interval: 1, byweekday: ['WE', 'TH'], }, @@ -276,7 +275,7 @@ describe('convertFromMaintenanceWindowToForm', () => { rRule: { dtstart: startDate.toISOString(), tzid: 'UTC', - freq: RRuleFrequency.MONTHLY, + freq: Frequency.MONTHLY, interval: 1, bymonthday: [22], }, @@ -305,7 +304,7 @@ describe('convertFromMaintenanceWindowToForm', () => { rRule: { dtstart: startDate.toISOString(), tzid: 'UTC', - freq: RRuleFrequency.YEARLY, + freq: Frequency.YEARLY, interval: 3, bymonth: [3], bymonthday: [22], diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_from_maintenance_window_to_form.ts b/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_from_maintenance_window_to_form.ts index 954e6b8bd2658..f29265c4c7f3a 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_from_maintenance_window_to_form.ts +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_from_maintenance_window_to_form.ts @@ -6,9 +6,10 @@ */ import moment from 'moment'; +import { Frequency } from '@kbn/rrule'; import { has } from 'lodash'; import { MaintenanceWindow } from '../types'; -import { EndsOptions, Frequency } from '../constants'; +import { EndsOptions, MaintenanceWindowFrequency } from '../constants'; import { FormProps, RecurringScheduleFormProps } from '../components/schema'; import { getInitialByWeekday } from './get_initial_by_weekday'; import { RRuleParams } from '../../../../common'; @@ -31,7 +32,7 @@ export const convertFromMaintenanceWindowToForm = ( const rRule = maintenanceWindow.rRule; const isCustomFrequency = isCustom(rRule); - const frequency = rRule.freq?.toString() as Frequency; + const frequency = rRule.freq as MaintenanceWindowFrequency; const ends = rRule.until ? EndsOptions.ON_DATE : rRule.count @@ -74,7 +75,7 @@ export const convertFromMaintenanceWindowToForm = ( }; const isCustom = (rRule: RRuleParams) => { - const freq = rRule.freq?.toString() as Frequency; + const freq = rRule.freq; // interval is greater than 1 if (rRule.interval && rRule.interval > 1) { return true; diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_to_rrule.test.ts b/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_to_rrule.test.ts index 7eaecba5169b8..bcbcccce06832 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_to_rrule.test.ts +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_to_rrule.test.ts @@ -6,9 +6,7 @@ */ import moment from 'moment'; - -import { Frequency } from '../constants'; -import { RRuleFrequency } from '../types'; +import { Frequency } from '@kbn/rrule'; import { convertToRRule } from './convert_to_rrule'; describe('convertToRRule', () => { @@ -22,7 +20,7 @@ describe('convertToRRule', () => { expect(rRule).toEqual({ dtstart: startDate.toISOString(), tzid: 'UTC', - freq: RRuleFrequency.YEARLY, + freq: Frequency.YEARLY, count: 1, }); }); @@ -37,7 +35,7 @@ describe('convertToRRule', () => { expect(rRule).toEqual({ dtstart: startDate.toISOString(), tzid: 'UTC', - freq: RRuleFrequency.DAILY, + freq: Frequency.DAILY, interval: 1, byweekday: ['WE'], }); @@ -55,7 +53,7 @@ describe('convertToRRule', () => { expect(rRule).toEqual({ dtstart: startDate.toISOString(), tzid: 'UTC', - freq: RRuleFrequency.DAILY, + freq: Frequency.DAILY, interval: 1, byweekday: ['WE'], until, @@ -73,7 +71,7 @@ describe('convertToRRule', () => { expect(rRule).toEqual({ dtstart: startDate.toISOString(), tzid: 'UTC', - freq: RRuleFrequency.DAILY, + freq: Frequency.DAILY, interval: 1, byweekday: ['WE'], count: 3, @@ -89,7 +87,7 @@ describe('convertToRRule', () => { expect(rRule).toEqual({ dtstart: startDate.toISOString(), tzid: 'UTC', - freq: RRuleFrequency.WEEKLY, + freq: Frequency.WEEKLY, interval: 1, byweekday: ['WE'], }); @@ -104,7 +102,7 @@ describe('convertToRRule', () => { expect(rRule).toEqual({ dtstart: startDate.toISOString(), tzid: 'UTC', - freq: RRuleFrequency.MONTHLY, + freq: Frequency.MONTHLY, interval: 1, byweekday: ['+4WE'], }); @@ -119,7 +117,7 @@ describe('convertToRRule', () => { expect(rRule).toEqual({ dtstart: startDate.toISOString(), tzid: 'UTC', - freq: RRuleFrequency.YEARLY, + freq: Frequency.YEARLY, interval: 1, bymonth: [3], bymonthday: [22], @@ -137,7 +135,7 @@ describe('convertToRRule', () => { expect(rRule).toEqual({ dtstart: startDate.toISOString(), tzid: 'UTC', - freq: RRuleFrequency.DAILY, + freq: Frequency.DAILY, interval: 1, }); }); @@ -154,7 +152,7 @@ describe('convertToRRule', () => { expect(rRule).toEqual({ dtstart: startDate.toISOString(), tzid: 'UTC', - freq: RRuleFrequency.WEEKLY, + freq: Frequency.WEEKLY, interval: 1, byweekday: ['WE', 'TH'], }); @@ -172,7 +170,7 @@ describe('convertToRRule', () => { expect(rRule).toEqual({ dtstart: startDate.toISOString(), tzid: 'UTC', - freq: RRuleFrequency.MONTHLY, + freq: Frequency.MONTHLY, interval: 1, bymonthday: [22], }); @@ -190,7 +188,7 @@ describe('convertToRRule', () => { expect(rRule).toEqual({ dtstart: startDate.toISOString(), tzid: 'UTC', - freq: RRuleFrequency.MONTHLY, + freq: Frequency.MONTHLY, interval: 1, byweekday: ['+4WE'], }); @@ -207,7 +205,7 @@ describe('convertToRRule', () => { expect(rRule).toEqual({ dtstart: startDate.toISOString(), tzid: 'UTC', - freq: RRuleFrequency.YEARLY, + freq: Frequency.YEARLY, interval: 3, bymonth: [3], bymonthday: [22], diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_to_rrule.ts b/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_to_rrule.ts index 90706165c717b..c3861a2a187ea 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_to_rrule.ts +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/convert_to_rrule.ts @@ -6,8 +6,8 @@ */ import { Moment } from 'moment'; -import { RRuleFrequency, RRuleFrequencyMap } from '../types'; -import { Frequency, ISO_WEEKDAYS_TO_RRULE } from '../constants'; +import { Frequency } from '@kbn/rrule'; +import { ISO_WEEKDAYS_TO_RRULE } from '../constants'; import { getNthByWeekday } from './get_nth_by_weekday'; import { RecurringScheduleFormProps } from '../components/schema'; import { getPresets } from './get_presets'; @@ -30,7 +30,7 @@ export const convertToRRule = ( ...rRule, // default to yearly and a count of 1 // if the maintenance window is not recurring - freq: RRuleFrequency.YEARLY, + freq: Frequency.YEARLY, count: 1, }; @@ -39,8 +39,8 @@ export const convertToRRule = ( form = { ...recurringForm, ...presets[recurringForm.frequency] }; } - const frequency = form.customFrequency ? form.customFrequency : (form.frequency as Frequency); - rRule.freq = RRuleFrequencyMap[frequency]; + const frequency = form.customFrequency ?? (form.frequency as Frequency); + rRule.freq = frequency; rRule.interval = form.interval; diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/get_presets.ts b/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/get_presets.ts index 737376b957c8a..529922eac5e87 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/get_presets.ts +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/get_presets.ts @@ -6,7 +6,7 @@ */ import { Moment } from 'moment'; -import { Frequency } from '../constants'; +import { Frequency } from '@kbn/rrule'; import { getInitialByWeekday } from './get_initial_by_weekday'; export const getPresets = (startDate: Moment) => { diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/recurring_summary.test.ts b/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/recurring_summary.test.ts index c889172ae9b44..7b6dc79483345 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/recurring_summary.test.ts +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/recurring_summary.test.ts @@ -7,7 +7,7 @@ import moment from 'moment'; -import { Frequency } from '../constants'; +import { Frequency } from '@kbn/rrule'; import { getPresets } from './get_presets'; import { recurringSummary } from './recurring_summary'; diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/recurring_summary.ts b/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/recurring_summary.ts index 69a3fc6f0ddda..5e3be96c04147 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/recurring_summary.ts +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/helpers/recurring_summary.ts @@ -6,8 +6,13 @@ */ import moment, { Moment } from 'moment'; +import { Frequency } from '@kbn/rrule'; import * as i18n from '../translations'; -import { Frequency, ISO_WEEKDAYS_TO_RRULE, RRULE_WEEKDAYS_TO_ISO_WEEKDAYS } from '../constants'; +import { + MaintenanceWindowFrequency, + ISO_WEEKDAYS_TO_RRULE, + RRULE_WEEKDAYS_TO_ISO_WEEKDAYS, +} from '../constants'; import { monthDayDate } from './month_day_date'; import { getNthByWeekday } from './get_nth_by_weekday'; import { RecurringScheduleFormProps } from '../components/schema'; @@ -15,7 +20,7 @@ import { RecurringScheduleFormProps } from '../components/schema'; export const recurringSummary = ( startDate: Moment, recurringSchedule: RecurringScheduleFormProps | undefined, - presets: Record> + presets: Record> ) => { if (!recurringSchedule) return ''; @@ -25,9 +30,8 @@ export const recurringSummary = ( schedule = { ...recurringSchedule, ...presets[recurringSchedule.frequency] }; } - const frequency = schedule.customFrequency - ? schedule.customFrequency - : (schedule.frequency as Frequency); + const frequency = + schedule.customFrequency ?? (schedule.frequency as MaintenanceWindowFrequency); const interval = schedule.interval || 1; const frequencySummary = i18n.CREATE_FORM_FREQUENCY_SUMMARY(interval)[frequency]; diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/translations.ts b/x-pack/plugins/alerting/public/pages/maintenance_windows/translations.ts index 32d234bfe1d36..0490d3faf54cf 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/translations.ts +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/translations.ts @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; import { Moment } from 'moment'; -import { Frequency } from 'rrule'; +import { Frequency } from '@kbn/rrule'; import { monthDayDate } from './helpers/month_day_date'; export const MAINTENANCE_WINDOWS = i18n.translate('xpack.alerting.maintenanceWindows', { diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/types.ts b/x-pack/plugins/alerting/public/pages/maintenance_windows/types.ts index e6be4af6b8675..6551d3808068a 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/types.ts +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/types.ts @@ -5,23 +5,17 @@ * 2.0. */ +import { Frequency } from '@kbn/rrule'; import { MaintenanceWindow as MaintenanceWindowServerSide, MaintenanceWindowModificationMetadata, } from '../../../common'; -export enum RRuleFrequency { - YEARLY = 0, - MONTHLY = 1, - WEEKLY = 2, - DAILY = 3, -} - export const RRuleFrequencyMap = { - '0': RRuleFrequency.YEARLY, - '1': RRuleFrequency.MONTHLY, - '2': RRuleFrequency.WEEKLY, - '3': RRuleFrequency.DAILY, + '0': Frequency.YEARLY, + '1': Frequency.MONTHLY, + '2': Frequency.WEEKLY, + '3': Frequency.DAILY, }; export type MaintenanceWindow = Pick; diff --git a/x-pack/plugins/alerting/server/lib/is_rule_snoozed.test.ts b/x-pack/plugins/alerting/server/lib/is_rule_snoozed.test.ts index 35ece5bd95c41..74baec92aa430 100644 --- a/x-pack/plugins/alerting/server/lib/is_rule_snoozed.test.ts +++ b/x-pack/plugins/alerting/server/lib/is_rule_snoozed.test.ts @@ -6,7 +6,8 @@ */ import sinon from 'sinon'; -import { RRule } from 'rrule'; +import moment from 'moment-timezone'; +import { Frequency } from '@kbn/rrule'; import { isRuleSnoozed } from './is_rule_snoozed'; import { RRuleRecord } from '../types'; @@ -14,6 +15,7 @@ const DATE_9999 = '9999-12-31T12:34:56.789Z'; const DATE_1970 = '1970-01-01T00:00:00.000Z'; const DATE_2019 = '2019-01-01T00:00:00.000Z'; const DATE_2019_PLUS_6_HOURS = '2019-01-01T06:00:00.000Z'; +const DATE_2019_IN_ASIA = '2018-12-31T16:00:00.000Z'; const DATE_2020 = '2020-01-01T00:00:00.000Z'; const DATE_2020_MINUS_1_HOUR = '2019-12-31T23:00:00.000Z'; const DATE_2020_MINUS_1_MONTH = '2019-12-01T00:00:00.000Z'; @@ -94,7 +96,7 @@ describe('isRuleSnoozed', () => { rRule: { dtstart: DATE_2019, tzid: 'UTC', - freq: RRule.DAILY, + freq: Frequency.DAILY, interval: 1, } as RRuleRecord, }, @@ -106,7 +108,7 @@ describe('isRuleSnoozed', () => { rRule: { dtstart: DATE_2019_PLUS_6_HOURS, tzid: 'UTC', - freq: RRule.DAILY, + freq: Frequency.DAILY, interval: 1, } as RRuleRecord, }, @@ -118,7 +120,7 @@ describe('isRuleSnoozed', () => { rRule: { dtstart: DATE_2020_MINUS_1_HOUR, tzid: 'UTC', - freq: RRule.HOURLY, + freq: Frequency.HOURLY, interval: 1, } as RRuleRecord, }, @@ -131,7 +133,7 @@ describe('isRuleSnoozed', () => { { duration: 60 * 1000, rRule: { - freq: RRule.HOURLY, + freq: Frequency.HOURLY, interval: 1, tzid: 'UTC', count: 8761, @@ -145,7 +147,7 @@ describe('isRuleSnoozed', () => { duration: 60 * 1000, rRule: { - freq: RRule.HOURLY, + freq: Frequency.HOURLY, interval: 1, tzid: 'UTC', count: 25, @@ -159,7 +161,7 @@ describe('isRuleSnoozed', () => { duration: 60 * 1000, rRule: { - freq: RRule.YEARLY, + freq: Frequency.YEARLY, interval: 1, tzid: 'UTC', count: 60, @@ -175,7 +177,7 @@ describe('isRuleSnoozed', () => { { duration: 60 * 1000, rRule: { - freq: RRule.HOURLY, + freq: Frequency.HOURLY, interval: 1, tzid: 'UTC', until: DATE_9999, @@ -188,7 +190,7 @@ describe('isRuleSnoozed', () => { { duration: 60 * 1000, rRule: { - freq: RRule.HOURLY, + freq: Frequency.HOURLY, interval: 1, tzid: 'UTC', until: DATE_2020_MINUS_1_HOUR, @@ -204,7 +206,7 @@ describe('isRuleSnoozed', () => { { duration: 60 * 1000, rRule: { - freq: RRule.WEEKLY, + freq: Frequency.WEEKLY, interval: 1, tzid: 'UTC', byweekday: ['MO', 'WE', 'FR'], // Jan 1 2020 was a Wednesday @@ -217,7 +219,7 @@ describe('isRuleSnoozed', () => { { duration: 60 * 1000, rRule: { - freq: RRule.WEEKLY, + freq: Frequency.WEEKLY, interval: 1, tzid: 'UTC', byweekday: ['TU', 'TH', 'SA', 'SU'], @@ -230,7 +232,7 @@ describe('isRuleSnoozed', () => { { duration: 60 * 1000, rRule: { - freq: RRule.WEEKLY, + freq: Frequency.WEEKLY, interval: 1, tzid: 'UTC', byweekday: ['MO', 'WE', 'FR'], @@ -244,7 +246,7 @@ describe('isRuleSnoozed', () => { { duration: 60 * 1000, rRule: { - freq: RRule.WEEKLY, + freq: Frequency.WEEKLY, interval: 1, tzid: 'UTC', byweekday: ['MO', 'WE', 'FR'], @@ -261,7 +263,7 @@ describe('isRuleSnoozed', () => { { duration: 60 * 1000, rRule: { - freq: RRule.MONTHLY, + freq: Frequency.MONTHLY, interval: 1, tzid: 'UTC', byweekday: ['+1WE'], // Jan 1 2020 was the first Wednesday of the month @@ -274,7 +276,7 @@ describe('isRuleSnoozed', () => { { duration: 60 * 1000, rRule: { - freq: RRule.MONTHLY, + freq: Frequency.MONTHLY, interval: 1, tzid: 'UTC', byweekday: ['+2WE'], @@ -286,35 +288,39 @@ describe('isRuleSnoozed', () => { }); test('using a timezone, returns as expected for a recurring snooze on a day of the week', () => { - const snoozeScheduleA = [ - { - duration: 60 * 1000, - rRule: { - freq: RRule.WEEKLY, - interval: 1, - byweekday: ['WE'], - tzid: 'Asia/Taipei', - dtstart: DATE_2019, - } as RRuleRecord, - }, - ]; + try { + const snoozeScheduleA = [ + { + duration: 60 * 1000, + rRule: { + freq: Frequency.WEEKLY, + interval: 1, + byweekday: ['WE'], + tzid: 'Asia/Taipei', + dtstart: DATE_2019_IN_ASIA, + } as RRuleRecord, + }, + ]; - expect(isRuleSnoozed({ snoozeSchedule: snoozeScheduleA, muteAll: false })).toBe(true); - const snoozeScheduleB = [ - { - duration: 60 * 1000, - rRule: { - freq: RRule.WEEKLY, - interval: 1, - byweekday: ['WE'], - byhour: [0], - byminute: [0], - tzid: 'UTC', - dtstart: DATE_2019, - } as RRuleRecord, - }, - ]; - expect(isRuleSnoozed({ snoozeSchedule: snoozeScheduleB, muteAll: false })).toBe(true); + expect(isRuleSnoozed({ snoozeSchedule: snoozeScheduleA, muteAll: false })).toBe(false); + const snoozeScheduleB = [ + { + duration: 60 * 1000, + rRule: { + freq: Frequency.WEEKLY, + interval: 1, + byweekday: ['WE'], + byhour: [0], + byminute: [0], + tzid: 'UTC', + dtstart: DATE_2019, + } as RRuleRecord, + }, + ]; + expect(isRuleSnoozed({ snoozeSchedule: snoozeScheduleB, muteAll: false })).toBe(true); + } catch (e) { + throw new Error(`In timezone ${process.env.TZ ?? moment.tz.guess()}: ${e}`); + } }); test('returns as expected for a manually skipped recurring snooze', () => { @@ -324,7 +330,7 @@ describe('isRuleSnoozed', () => { rRule: { dtstart: DATE_2019, tzid: 'UTC', - freq: RRule.DAILY, + freq: Frequency.DAILY, interval: 1, } as RRuleRecord, skipRecurrences: [DATE_2020], @@ -337,7 +343,7 @@ describe('isRuleSnoozed', () => { rRule: { dtstart: DATE_2019, tzid: 'UTC', - freq: RRule.DAILY, + freq: Frequency.DAILY, interval: 1, } as RRuleRecord, skipRecurrences: [DATE_2020_MINUS_1_MONTH], @@ -350,7 +356,7 @@ describe('isRuleSnoozed', () => { rRule: { dtstart: DATE_2020, tzid: 'UTC', - freq: RRule.DAILY, + freq: Frequency.DAILY, interval: 1, } as RRuleRecord, skipRecurrences: [DATE_2020], @@ -363,7 +369,7 @@ describe('isRuleSnoozed', () => { rRule: { dtstart: DATE_2020_MINUS_6_HOURS, tzid: 'UTC', - freq: RRule.HOURLY, + freq: Frequency.HOURLY, interval: 5, } as RRuleRecord, skipRecurrences: [DATE_2020_MINUS_1_HOUR], diff --git a/x-pack/plugins/alerting/server/lib/rrule/index.ts b/x-pack/plugins/alerting/server/lib/rrule/index.ts deleted file mode 100644 index 22674b91e6ee4..0000000000000 --- a/x-pack/plugins/alerting/server/lib/rrule/index.ts +++ /dev/null @@ -1,8 +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 * from './parse_by_weekday'; diff --git a/x-pack/plugins/alerting/server/lib/rrule/parse_by_weekday.ts b/x-pack/plugins/alerting/server/lib/rrule/parse_by_weekday.ts deleted file mode 100644 index 9d9cdf3c58848..0000000000000 --- a/x-pack/plugins/alerting/server/lib/rrule/parse_by_weekday.ts +++ /dev/null @@ -1,13 +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 { ByWeekday, rrulestr } from 'rrule'; - -export function parseByWeekday(byweekday: Array): ByWeekday[] { - const rRuleString = `RRULE:BYDAY=${byweekday.join(',')}`; - const parsedRRule = rrulestr(rRuleString); - return parsedRRule.origOptions.byweekday as ByWeekday[]; -} diff --git a/x-pack/plugins/alerting/server/lib/snooze/index.ts b/x-pack/plugins/alerting/server/lib/snooze/index.ts index beb38a3f9650b..6c04be20ca97c 100644 --- a/x-pack/plugins/alerting/server/lib/snooze/index.ts +++ b/x-pack/plugins/alerting/server/lib/snooze/index.ts @@ -7,4 +7,3 @@ export { isSnoozeActive } from './is_snooze_active'; export { isSnoozeExpired } from './is_snooze_expired'; -export { utcToLocalUtc, localUtcToUtc } from './timezone_helpers'; diff --git a/x-pack/plugins/alerting/server/lib/snooze/is_snooze_active.test.ts b/x-pack/plugins/alerting/server/lib/snooze/is_snooze_active.test.ts index d42824d6e840a..9fdce331a7da9 100644 --- a/x-pack/plugins/alerting/server/lib/snooze/is_snooze_active.test.ts +++ b/x-pack/plugins/alerting/server/lib/snooze/is_snooze_active.test.ts @@ -6,20 +6,20 @@ */ import moment from 'moment'; -import { RRule } from 'rrule'; +import { Frequency } from '@kbn/rrule'; import sinon from 'sinon'; import { RRuleRecord } from '../../types'; import { isSnoozeActive } from './is_snooze_active'; let fakeTimer: sinon.SinonFakeTimers; -describe('isSnoozeExpired', () => { +describe('isSnoozeActive', () => { afterAll(() => fakeTimer.restore()); test('snooze is NOT active byweekday', () => { // Set the current time as: // - Feb 27 2023 08:15:00 GMT+0000 - Monday - fakeTimer = sinon.useFakeTimers(new Date('2023-02-27T06:15:00.000Z')); + fakeTimer = sinon.useFakeTimers(new Date('2023-02-27T08:15:00.000Z')); // Try to get snooze end time with: // - Start date of: Feb 24 2023 23:00:00 GMT+0000 - Friday @@ -30,7 +30,7 @@ describe('isSnoozeExpired', () => { rRule: { byweekday: ['SA'], tzid: 'Europe/Madrid', - freq: RRule.DAILY, + freq: Frequency.DAILY, interval: 1, dtstart: '2023-02-24T23:00:00.000Z', } as RRuleRecord, @@ -54,7 +54,7 @@ describe('isSnoozeExpired', () => { rRule: { byweekday: ['SA'], tzid: 'Europe/Madrid', - freq: RRule.DAILY, + freq: Frequency.DAILY, interval: 1, dtstart: '2023-02-24T23:00:00.000Z', } as RRuleRecord, @@ -84,7 +84,7 @@ describe('isSnoozeExpired', () => { rRule: { byweekday: ['SA'], tzid: 'Europe/Madrid', - freq: RRule.DAILY, + freq: Frequency.DAILY, interval: 1, dtstart: '2023-02-24T23:00:00.000Z', } as RRuleRecord, @@ -108,7 +108,7 @@ describe('isSnoozeExpired', () => { rRule: { byweekday: ['SA'], tzid: 'Europe/Madrid', - freq: RRule.DAILY, + freq: Frequency.DAILY, interval: 1, dtstart: '2023-02-24T23:00:00.000Z', } as RRuleRecord, @@ -117,7 +117,7 @@ describe('isSnoozeExpired', () => { expect(isSnoozeActive(snoozeA)).toMatchInlineSnapshot(` Object { "id": "9141dc1f-ed85-4656-91e4-119173105432", - "lastOccurrence": 2023-03-04T00:00:00.000Z, + "lastOccurrence": 2023-03-03T23:00:00.000Z, "snoozeEndTime": 2023-03-06T06:00:00.000Z, } `); @@ -136,7 +136,7 @@ describe('isSnoozeExpired', () => { const snoozeA = { duration: moment('2023-01', 'YYYY-MM').daysInMonth() * 24 * 60 * 60 * 1000, // 1 month rRule: { - freq: 0, + freq: Frequency.YEARLY, interval: 1, bymonthday: [1], bymonth: [1], @@ -164,7 +164,7 @@ describe('isSnoozeExpired', () => { bymonthday: [1], bymonth: [1], tzid: 'Europe/Madrid', - freq: RRule.MONTHLY, + freq: Frequency.MONTHLY, interval: 1, dtstart: '2023-01-01T00:00:00.000Z', } as RRuleRecord, @@ -195,7 +195,7 @@ describe('isSnoozeExpired', () => { bymonthday: [1], bymonth: [1], tzid: 'Europe/Madrid', - freq: RRule.MONTHLY, + freq: Frequency.MONTHLY, interval: 1, dtstart: '2023-01-01T00:00:00.000Z', } as RRuleRecord, @@ -221,7 +221,7 @@ describe('isSnoozeExpired', () => { bymonthday: [1], bymonth: [1], tzid: 'Europe/Madrid', - freq: RRule.MONTHLY, + freq: Frequency.MONTHLY, interval: 1, dtstart: '2023-01-01T00:00:00.000Z', } as RRuleRecord, diff --git a/x-pack/plugins/alerting/server/lib/snooze/is_snooze_active.ts b/x-pack/plugins/alerting/server/lib/snooze/is_snooze_active.ts index 3013cf12ae71f..0af968c52e206 100644 --- a/x-pack/plugins/alerting/server/lib/snooze/is_snooze_active.ts +++ b/x-pack/plugins/alerting/server/lib/snooze/is_snooze_active.ts @@ -5,10 +5,8 @@ * 2.0. */ -import { RRule, Weekday } from 'rrule'; +import { RRule, Weekday } from '@kbn/rrule'; import { RuleSnoozeSchedule } from '../../types'; -import { parseByWeekday } from '../rrule'; -import { utcToLocalUtc, localUtcToUtc } from './timezone_helpers'; const MAX_TIMESTAMP = 8640000000000000; @@ -32,32 +30,24 @@ export function isSnoozeActive(snooze: RuleSnoozeSchedule) { }; // Check to see if now is during a recurrence of the snooze - - const { tzid, ...restRRule } = rRule; - const startDate = utcToLocalUtc(new Date(rRule.dtstart), tzid); - const nowDate = utcToLocalUtc(new Date(now), tzid); - try { const rRuleOptions = { - ...restRRule, - dtstart: startDate, - until: rRule.until ? utcToLocalUtc(new Date(rRule.until), tzid) : null, - wkst: rRule.wkst ? Weekday.fromStr(rRule.wkst) : null, - byweekday: rRule.byweekday ? parseByWeekday(rRule.byweekday) : null, + ...rRule, + dtstart: new Date(rRule.dtstart), + until: rRule.until ? new Date(rRule.until) : null, + byweekday: rRule.byweekday ?? null, + wkst: rRule.wkst ? Weekday[rRule.wkst] : null, }; const recurrenceRule = new RRule(rRuleOptions); - const lastOccurrence = recurrenceRule.before(nowDate, true); + const lastOccurrence = recurrenceRule.before(new Date(now)); + if (!lastOccurrence) return null; // Check if the current recurrence has been skipped manually if (snooze.skipRecurrences?.includes(lastOccurrence.toISOString())) return null; const lastOccurrenceEndTime = lastOccurrence.getTime() + duration; - if (nowDate.getTime() < lastOccurrenceEndTime) - return { - lastOccurrence, - snoozeEndTime: localUtcToUtc(new Date(lastOccurrenceEndTime), tzid), - id, - }; + if (now < lastOccurrenceEndTime) + return { lastOccurrence, snoozeEndTime: new Date(lastOccurrenceEndTime), id }; } catch (e) { throw new Error(`Failed to process RRule ${rRule}: ${e}`); } diff --git a/x-pack/plugins/alerting/server/lib/snooze/is_snooze_expired.test.ts b/x-pack/plugins/alerting/server/lib/snooze/is_snooze_expired.test.ts index 3dfee044cd1c3..f0a516e855aab 100644 --- a/x-pack/plugins/alerting/server/lib/snooze/is_snooze_expired.test.ts +++ b/x-pack/plugins/alerting/server/lib/snooze/is_snooze_expired.test.ts @@ -6,7 +6,7 @@ */ import sinon from 'sinon'; -import { RRule } from 'rrule'; +import { Frequency } from '@kbn/rrule'; import { isSnoozeExpired } from './is_snooze_expired'; import { RRuleRecord } from '../../types'; @@ -84,7 +84,7 @@ describe('isSnoozeExpired', () => { rRule: { dtstart: DATE_2019, tzid: 'UTC', - freq: RRule.DAILY, + freq: Frequency.DAILY, interval: 1, } as RRuleRecord, }; @@ -95,7 +95,7 @@ describe('isSnoozeExpired', () => { rRule: { dtstart: DATE_2019_PLUS_6_HOURS, tzid: 'UTC', - freq: RRule.DAILY, + freq: Frequency.DAILY, interval: 1, } as RRuleRecord, }; @@ -105,7 +105,7 @@ describe('isSnoozeExpired', () => { rRule: { dtstart: DATE_2020_MINUS_1_HOUR, tzid: 'UTC', - freq: RRule.HOURLY, + freq: Frequency.HOURLY, interval: 1, } as RRuleRecord, }; @@ -116,7 +116,7 @@ describe('isSnoozeExpired', () => { const snoozeA = { duration: 60 * 1000, rRule: { - freq: RRule.HOURLY, + freq: Frequency.HOURLY, interval: 1, tzid: 'UTC', count: 8761, @@ -128,7 +128,7 @@ describe('isSnoozeExpired', () => { duration: 60 * 1000, rRule: { - freq: RRule.HOURLY, + freq: Frequency.HOURLY, interval: 1, tzid: 'UTC', count: 25, @@ -140,7 +140,7 @@ describe('isSnoozeExpired', () => { duration: 60 * 1000, rRule: { - freq: RRule.YEARLY, + freq: Frequency.YEARLY, interval: 1, tzid: 'UTC', count: 30, @@ -155,7 +155,7 @@ describe('isSnoozeExpired', () => { const snoozeA = { duration: 60 * 1000, rRule: { - freq: RRule.HOURLY, + freq: Frequency.HOURLY, interval: 1, tzid: 'UTC', until: DATE_9999, @@ -166,7 +166,7 @@ describe('isSnoozeExpired', () => { const snoozeB = { duration: 60 * 1000, rRule: { - freq: RRule.HOURLY, + freq: Frequency.HOURLY, interval: 1, tzid: 'UTC', until: DATE_2020_MINUS_1_HOUR, diff --git a/x-pack/plugins/alerting/server/lib/snooze/is_snooze_expired.ts b/x-pack/plugins/alerting/server/lib/snooze/is_snooze_expired.ts index 3c15074091098..f9dd80f9b2024 100644 --- a/x-pack/plugins/alerting/server/lib/snooze/is_snooze_expired.ts +++ b/x-pack/plugins/alerting/server/lib/snooze/is_snooze_expired.ts @@ -5,10 +5,9 @@ * 2.0. */ -import { RRule, Weekday } from 'rrule'; +import { RRule, Weekday } from '@kbn/rrule'; import { RuleSnoozeSchedule } from '../../types'; import { isSnoozeActive } from './is_snooze_active'; -import { parseByWeekday } from '../rrule'; export function isSnoozeExpired(snooze: RuleSnoozeSchedule) { if (isSnoozeActive(snooze)) { @@ -23,12 +22,12 @@ export function isSnoozeExpired(snooze: RuleSnoozeSchedule) { ...rRule, dtstart: new Date(rRule.dtstart), until: rRule.until ? new Date(rRule.until) : null, - wkst: rRule.wkst ? Weekday.fromStr(rRule.wkst) : null, - byweekday: rRule.byweekday ? parseByWeekday(rRule.byweekday) : null, + byweekday: rRule.byweekday ?? null, + wkst: rRule.wkst ? Weekday[rRule.wkst] : null, }; const recurrenceRule = new RRule(rRuleOptions); - const nextOccurrence = recurrenceRule.after(new Date(now), true); + const nextOccurrence = recurrenceRule.after(new Date(now)); return !nextOccurrence; } catch (e) { throw new Error(`Failed to process RRule ${rRule}: ${e}`); diff --git a/x-pack/plugins/alerting/server/lib/snooze/timezone_helpers.ts b/x-pack/plugins/alerting/server/lib/snooze/timezone_helpers.ts deleted file mode 100644 index e32e8623103bd..0000000000000 --- a/x-pack/plugins/alerting/server/lib/snooze/timezone_helpers.ts +++ /dev/null @@ -1,32 +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 moment from 'moment-timezone'; - -/** - * Converts the UTC date into the user's local time zone, but still in UTC. - * This must be done because rrule does not care about timezones, so for the result - * to be correct, we must ensure everything is timezone agnostic. - * - * example: 2023-03-29 08:00:00 CET -> 2023-03-29 08:00:00 UTC - */ -export const utcToLocalUtc = (date: Date, tz: string) => { - const localTime = moment(date).tz(tz); - const localTimeInUTC = moment(localTime).tz('UTC', true); - return localTimeInUTC.utc().toDate(); -}; - -/** - * Converts the local date in UTC back into actual UTC. After rrule does its thing, - * we would still like to keep everything in UTC in the business logic, hence why we - * need to convert everything back - * - * Example: 2023-03-29 08:00:00 UTC (from the utcToLocalUtc output) -> 2023-03-29 06:00:00 UTC (Real UTC) - */ -export const localUtcToUtc = (date: Date, tz: string) => { - const localTimeString = moment.utc(date).format('YYYY-MM-DD HH:mm:ss.SSS'); - return moment.tz(localTimeString, tz).utc().toDate(); -}; diff --git a/x-pack/plugins/alerting/server/lib/validate_snooze_schedule.ts b/x-pack/plugins/alerting/server/lib/validate_snooze_schedule.ts index 4004a40579253..a6e9059be9857 100644 --- a/x-pack/plugins/alerting/server/lib/validate_snooze_schedule.ts +++ b/x-pack/plugins/alerting/server/lib/validate_snooze_schedule.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Frequency } from 'rrule'; +import { Frequency } from '@kbn/rrule'; import moment from 'moment'; import { RuleSnoozeSchedule } from '../types'; diff --git a/x-pack/plugins/alerting/server/maintenance_window_client/generate_maintenance_window_events.test.ts b/x-pack/plugins/alerting/server/maintenance_window_client/generate_maintenance_window_events.test.ts index 85cfc0f134c4b..c7e15cf1f974a 100644 --- a/x-pack/plugins/alerting/server/maintenance_window_client/generate_maintenance_window_events.test.ts +++ b/x-pack/plugins/alerting/server/maintenance_window_client/generate_maintenance_window_events.test.ts @@ -6,7 +6,7 @@ */ import moment from 'moment-timezone'; -import { RRule } from 'rrule'; +import { Frequency } from '@kbn/rrule'; import { generateMaintenanceWindowEvents } from './generate_maintenance_window_events'; describe('generateMaintenanceWindowEvents', () => { @@ -24,7 +24,7 @@ describe('generateMaintenanceWindowEvents', () => { .toISOString(), rRule: { tzid: 'UTC', - freq: RRule.DAILY, + freq: Frequency.DAILY, interval: 1, dtstart: '2023-02-27T00:00:00.000Z', }, @@ -55,7 +55,7 @@ describe('generateMaintenanceWindowEvents', () => { .toISOString(), rRule: { tzid: 'UTC', - freq: RRule.WEEKLY, + freq: Frequency.WEEKLY, interval: 1, dtstart: '2023-02-27T00:00:00.000Z', }, @@ -86,7 +86,7 @@ describe('generateMaintenanceWindowEvents', () => { .toISOString(), rRule: { tzid: 'UTC', - freq: RRule.MONTHLY, + freq: Frequency.MONTHLY, interval: 1, dtstart: '2023-02-27T00:00:00.000Z', }, @@ -116,7 +116,7 @@ describe('generateMaintenanceWindowEvents', () => { .toISOString(), rRule: { tzid: 'UTC', - freq: RRule.WEEKLY, + freq: Frequency.WEEKLY, interval: 1, byweekday: ['TU', 'TH'], dtstart: '2023-02-27T00:00:00.000Z', diff --git a/x-pack/plugins/alerting/server/maintenance_window_client/generate_maintenance_window_events.ts b/x-pack/plugins/alerting/server/maintenance_window_client/generate_maintenance_window_events.ts index aa09b0069ce49..92ad3ad0fda4d 100644 --- a/x-pack/plugins/alerting/server/maintenance_window_client/generate_maintenance_window_events.ts +++ b/x-pack/plugins/alerting/server/maintenance_window_client/generate_maintenance_window_events.ts @@ -7,10 +7,8 @@ import _ from 'lodash'; import moment from 'moment-timezone'; -import { RRule, Weekday } from 'rrule'; -import { parseByWeekday } from '../lib/rrule'; +import { RRule, Weekday } from '@kbn/rrule'; import { RRuleParams, MaintenanceWindowSOAttributes, DateRange } from '../../common'; -import { utcToLocalUtc, localUtcToUtc } from '../lib/snooze'; export interface GenerateMaintenanceWindowEventsParams { rRule: RRuleParams; @@ -23,28 +21,27 @@ export const generateMaintenanceWindowEvents = ({ expirationDate, duration, }: GenerateMaintenanceWindowEventsParams) => { - const { dtstart, until, wkst, byweekday, tzid, ...rest } = rRule; + const { dtstart, until, wkst, byweekday, ...rest } = rRule; - const startDate = utcToLocalUtc(new Date(dtstart), tzid); - const endDate = utcToLocalUtc(new Date(expirationDate), tzid); + const startDate = new Date(dtstart); + const endDate = new Date(expirationDate); const rRuleOptions = { ...rest, dtstart: startDate, - until: until ? utcToLocalUtc(new Date(until), tzid) : null, - wkst: wkst ? Weekday.fromStr(wkst) : null, - byweekday: byweekday ? parseByWeekday(byweekday) : null, + until: until ? new Date(until) : null, + wkst: wkst ? Weekday[wkst] : null, + byweekday: byweekday ?? null, }; try { const recurrenceRule = new RRule(rRuleOptions); - const occurrenceDates = recurrenceRule.between(startDate, endDate, true); + const occurrenceDates = recurrenceRule.between(startDate, endDate); return occurrenceDates.map((date) => { - const utcDate = localUtcToUtc(date, tzid); return { - gte: utcDate.toISOString(), - lte: moment.utc(utcDate).add(duration, 'ms').toISOString(), + gte: date.toISOString(), + lte: moment(date).add(duration, 'ms').toISOString(), }; }); } catch (e) { diff --git a/x-pack/plugins/alerting/server/maintenance_window_client/methods/archive.test.ts b/x-pack/plugins/alerting/server/maintenance_window_client/methods/archive.test.ts index 0bdf0748921f7..c3da9c5948206 100644 --- a/x-pack/plugins/alerting/server/maintenance_window_client/methods/archive.test.ts +++ b/x-pack/plugins/alerting/server/maintenance_window_client/methods/archive.test.ts @@ -6,7 +6,7 @@ */ import moment from 'moment-timezone'; -import { RRule } from 'rrule'; +import { Frequency } from '@kbn/rrule'; import { archive } from './archive'; import { savedObjectsClientMock, loggingSystemMock } from '@kbn/core/server/mocks'; import { SavedObjectsUpdateResponse, SavedObject } from '@kbn/core/server'; @@ -153,7 +153,7 @@ describe('MaintenanceWindowClient - archive', () => { rRule: { tzid: 'CET', dtstart: '2023-03-26T00:00:00.000Z', - freq: RRule.WEEKLY, + freq: Frequency.WEEKLY, count: 5, }, events: modifiedEvents, diff --git a/x-pack/plugins/alerting/server/maintenance_window_client/methods/finish.test.ts b/x-pack/plugins/alerting/server/maintenance_window_client/methods/finish.test.ts index 8eb4d812426d4..8a37f228892b6 100644 --- a/x-pack/plugins/alerting/server/maintenance_window_client/methods/finish.test.ts +++ b/x-pack/plugins/alerting/server/maintenance_window_client/methods/finish.test.ts @@ -6,7 +6,7 @@ */ import moment from 'moment-timezone'; -import { RRule } from 'rrule'; +import { Frequency } from '@kbn/rrule'; import { finish } from './finish'; import { savedObjectsClientMock, loggingSystemMock } from '@kbn/core/server/mocks'; import { SavedObjectsUpdateResponse, SavedObject } from '@kbn/core/server'; @@ -54,7 +54,7 @@ describe('MaintenanceWindowClient - finish', () => { rRule: { tzid: 'UTC', dtstart: moment().utc().toISOString(), - freq: RRule.WEEKLY, + freq: Frequency.WEEKLY, count: 2, }, }); @@ -110,7 +110,7 @@ describe('MaintenanceWindowClient - finish', () => { rRule: { tzid: 'CET', dtstart: '2023-03-26T00:00:00.000Z', - freq: RRule.WEEKLY, + freq: Frequency.WEEKLY, count: 5, }, events: modifiedEvents, @@ -158,7 +158,7 @@ describe('MaintenanceWindowClient - finish', () => { rRule: { tzid: 'UTC', dtstart: moment().utc().toISOString(), - freq: RRule.WEEKLY, + freq: Frequency.WEEKLY, count: 2, }, }); diff --git a/x-pack/plugins/alerting/server/maintenance_window_client/methods/test_helpers.ts b/x-pack/plugins/alerting/server/maintenance_window_client/methods/test_helpers.ts index c07497135e4c9..54745c61b73e9 100644 --- a/x-pack/plugins/alerting/server/maintenance_window_client/methods/test_helpers.ts +++ b/x-pack/plugins/alerting/server/maintenance_window_client/methods/test_helpers.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { RRule } from 'rrule'; +import { Frequency } from '@kbn/rrule'; import { MaintenanceWindowSOAttributes } from '../../../common'; export const getMockMaintenanceWindow = ( @@ -18,7 +18,7 @@ export const getMockMaintenanceWindow = ( rRule: { tzid: 'UTC', dtstart: '2023-02-26T00:00:00.000Z', - freq: RRule.WEEKLY, + freq: Frequency.WEEKLY, count: 2, }, events: [ diff --git a/x-pack/plugins/alerting/server/maintenance_window_client/methods/update.test.ts b/x-pack/plugins/alerting/server/maintenance_window_client/methods/update.test.ts index 715d74dd9f901..ea2cb33341b0d 100644 --- a/x-pack/plugins/alerting/server/maintenance_window_client/methods/update.test.ts +++ b/x-pack/plugins/alerting/server/maintenance_window_client/methods/update.test.ts @@ -6,7 +6,7 @@ */ import moment from 'moment-timezone'; -import { RRule } from 'rrule'; +import { Frequency } from '@kbn/rrule'; import { update } from './update'; import { savedObjectsClientMock, loggingSystemMock } from '@kbn/core/server/mocks'; import { SavedObject } from '@kbn/core/server'; @@ -28,7 +28,7 @@ const updatedAttributes = { rRule: { tzid: 'CET', dtstart: '2023-03-26T00:00:00.000Z', - freq: RRule.WEEKLY, + freq: Frequency.WEEKLY, count: 2, }, }; @@ -134,7 +134,7 @@ describe('MaintenanceWindowClient - update', () => { rRule: { tzid: 'CET', dtstart: '2023-03-26T00:00:00.000Z', - freq: RRule.WEEKLY, + freq: Frequency.WEEKLY, count: 5, }, events: modifiedEvents, @@ -177,7 +177,7 @@ describe('MaintenanceWindowClient - update', () => { rRule: { tzid: 'CET', dtstart: '2023-03-26T00:00:00.000Z', - freq: RRule.WEEKLY, + freq: Frequency.WEEKLY, count: 2, }, }); diff --git a/x-pack/plugins/alerting/server/routes/lib/rrule_schema.ts b/x-pack/plugins/alerting/server/routes/lib/rrule_schema.ts index dc518d64df0f4..4f714ef8d9fbe 100644 --- a/x-pack/plugins/alerting/server/routes/lib/rrule_schema.ts +++ b/x-pack/plugins/alerting/server/routes/lib/rrule_schema.ts @@ -13,7 +13,11 @@ export const rRuleSchema = schema.object({ dtstart: schema.string({ validate: validateSnoozeStartDate }), tzid: schema.string(), freq: schema.maybe( - schema.oneOf([schema.literal(0), schema.literal(1), schema.literal(2), schema.literal(3)]) + schema.number({ + validate: (freq: number) => { + if (freq < 0 || freq > 3) return 'rRule freq must be 0, 1, 2, or 3'; + }, + }) ), interval: schema.maybe( schema.number({ diff --git a/x-pack/plugins/alerting/server/routes/maintenance_window/update_maintenance_window.test.ts b/x-pack/plugins/alerting/server/routes/maintenance_window/update_maintenance_window.test.ts index f8f34d1701caa..a7793c4995890 100644 --- a/x-pack/plugins/alerting/server/routes/maintenance_window/update_maintenance_window.test.ts +++ b/x-pack/plugins/alerting/server/routes/maintenance_window/update_maintenance_window.test.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { RRule } from 'rrule'; +import { Frequency } from '@kbn/rrule'; import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../../lib/license_state.mock'; import { verifyApiAccess } from '../../lib/license_api_access'; @@ -36,7 +36,7 @@ const updateParams = { r_rule: { tzid: 'CET', dtstart: '2023-03-26T00:00:00.000Z', - freq: RRule.WEEKLY, + freq: Frequency.WEEKLY, count: 10, }, }; diff --git a/x-pack/plugins/alerting/tsconfig.json b/x-pack/plugins/alerting/tsconfig.json index cdba5b7a4d8e6..9cafbe064360c 100644 --- a/x-pack/plugins/alerting/tsconfig.json +++ b/x-pack/plugins/alerting/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../../tsconfig.base.json", "compilerOptions": { - "outDir": "target/types", + "outDir": "target/types" }, "include": [ "server/**/*", @@ -42,6 +42,7 @@ "@kbn/alerting-state-types", "@kbn/alerts-as-data-utils", "@kbn/core-elasticsearch-client-server-mocks", + "@kbn/rrule", "@kbn/shared-ux-router", "@kbn/kibana-react-plugin", "@kbn/management-plugin", @@ -52,9 +53,7 @@ "@kbn/doc-links", "@kbn/core-saved-objects-utils-server", "@kbn/core-ui-settings-common", - "@kbn/core-capabilities-common", + "@kbn/core-capabilities-common" ], - "exclude": [ - "target/**/*", - ] + "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/cases/common/api/cases/case.ts b/x-pack/plugins/cases/common/api/cases/case.ts index acfca22e70498..fd734c943de0e 100644 --- a/x-pack/plugins/cases/common/api/cases/case.ts +++ b/x-pack/plugins/cases/common/api/cases/case.ts @@ -13,12 +13,18 @@ import { CommentRt } from './comment'; import { CasesStatusResponseRt, CaseStatusRt } from './status'; import { CaseConnectorRt } from '../connectors/connector'; import { CaseAssigneesRt } from './assignee'; +import { limitedArraySchema, limitedStringSchema, NonEmptyString } from '../../schema'; import { + MAX_DELETE_IDS_LENGTH, + MAX_DESCRIPTION_LENGTH, + MAX_TITLE_LENGTH, + MAX_LENGTH_PER_TAG, + MAX_CATEGORY_LENGTH, + MAX_TAGS_PER_CASE, MAX_ASSIGNEES_FILTER_LENGTH, MAX_REPORTERS_FILTER_LENGTH, MAX_TAGS_FILTER_LENGTH, } from '../../constants'; -import { limitedArraySchema } from '../../schema'; export const AttachmentTotalsRt = rt.strict({ alerts: rt.number, @@ -138,15 +144,20 @@ export const CasePostRequestRt = rt.intersection([ /** * Description of the case */ - description: rt.string, + description: limitedStringSchema('description', 1, MAX_DESCRIPTION_LENGTH), /** * Identifiers for the case. */ - tags: rt.array(rt.string), + tags: limitedArraySchema( + limitedStringSchema('tag', 1, MAX_LENGTH_PER_TAG), + 0, + MAX_TAGS_PER_CASE, + 'tags' + ), /** * Title of the case */ - title: rt.string, + title: limitedStringSchema('title', 1, MAX_TITLE_LENGTH), /** * The external configuration for the case */ @@ -175,7 +186,7 @@ export const CasePostRequestRt = rt.intersection([ /** * The category of the case. */ - category: rt.union([rt.string, rt.null]), + category: rt.union([limitedStringSchema('category', 1, MAX_CATEGORY_LENGTH), rt.null]), }) ), ]); @@ -295,6 +306,13 @@ export const CasesFindRequestRt = rt.exact( }) ); +export const CasesDeleteRequestRt = limitedArraySchema( + NonEmptyString, + 1, + MAX_DELETE_IDS_LENGTH, + 'ids' +); + export const CasesByAlertIDRequestRt = rt.exact( rt.partial({ /** @@ -347,7 +365,55 @@ export const CasesFindResponseRt = rt.intersection([ ]); export const CasePatchRequestRt = rt.intersection([ - rt.exact(rt.partial(CaseBasicRt.type.props)), + rt.exact( + rt.partial({ + /** + * The description of the case + */ + description: limitedStringSchema('description', 1, MAX_DESCRIPTION_LENGTH), + /** + * The current status of the case (open, closed, in-progress) + */ + status: CaseStatusRt, + /** + * The identifying strings for filter a case + */ + tags: limitedArraySchema( + limitedStringSchema('tag', 1, MAX_LENGTH_PER_TAG), + 0, + MAX_TAGS_PER_CASE, + 'tags' + ), + /** + * The title of a case + */ + title: limitedStringSchema('title', 1, MAX_TITLE_LENGTH), + /** + * The external system that the case can be synced with + */ + connector: CaseConnectorRt, + /** + * The alert sync settings + */ + settings: SettingsRt, + /** + * The plugin owner of the case + */ + owner: rt.string, + /** + * The severity of the case + */ + severity: CaseSeverityRt, + /** + * The users assigned to this case + */ + assignees: CaseAssigneesRt, + /** + * The category of the case. + */ + category: rt.union([limitedStringSchema('category', 1, MAX_CATEGORY_LENGTH), rt.null]), + }) + ), /** * The saved object ID and version */ @@ -432,6 +498,7 @@ export type CasePostRequest = rt.TypeOf; export type Case = rt.TypeOf; export type CaseResolveResponse = rt.TypeOf; export type Cases = rt.TypeOf; +export type CasesDeleteRequest = rt.TypeOf; export type CasesFindRequest = rt.TypeOf; export type CasesByAlertIDRequest = rt.TypeOf; export type CasesFindResponse = rt.TypeOf; diff --git a/x-pack/plugins/cases/common/constants/index.ts b/x-pack/plugins/cases/common/constants/index.ts index 24365cdc625e8..bee8ff9f3b17e 100644 --- a/x-pack/plugins/cases/common/constants/index.ts +++ b/x-pack/plugins/cases/common/constants/index.ts @@ -116,6 +116,10 @@ export const MAX_REPORTERS_FILTER_LENGTH = 100 as const; export const MAX_TITLE_LENGTH = 160 as const; export const MAX_CATEGORY_LENGTH = 50 as const; +export const MAX_DESCRIPTION_LENGTH = 30000 as const; +export const MAX_LENGTH_PER_TAG = 256 as const; +export const MAX_TAGS_PER_CASE = 200 as const; +export const MAX_DELETE_IDS_LENGTH = 100 as const; /** * Cases features diff --git a/x-pack/plugins/cases/common/schema/index.test.ts b/x-pack/plugins/cases/common/schema/index.test.ts index 3f1cfa549955f..826ee6ffbcd31 100644 --- a/x-pack/plugins/cases/common/schema/index.test.ts +++ b/x-pack/plugins/cases/common/schema/index.test.ts @@ -7,70 +7,141 @@ import { PathReporter } from 'io-ts/lib/PathReporter'; -import { limitedArraySchema, NonEmptyString } from '.'; +import { limitedArraySchema, limitedStringSchema, NonEmptyString } from '.'; describe('schema', () => { - it('fails when given an empty string', () => { - expect(PathReporter.report(limitedArraySchema(NonEmptyString, 1, 1).decode(['']))) - .toMatchInlineSnapshot(` - Array [ - "string must have length >= 1", - ] - `); - }); + describe('limitedArraySchema', () => { + const fieldName = 'foobar'; - it('fails when given an empty array', () => { - expect(PathReporter.report(limitedArraySchema(NonEmptyString, 1, 1).decode([]))) - .toMatchInlineSnapshot(` + it('fails when given an empty string', () => { + expect(PathReporter.report(limitedArraySchema(NonEmptyString, 1, 1, fieldName).decode(['']))) + .toMatchInlineSnapshot(` Array [ - "Array must be of length >= 1.", - ] - `); - }); - - it('fails when given an array larger than the limit of one item', () => { - expect(PathReporter.report(limitedArraySchema(NonEmptyString, 1, 1).decode(['a', 'b']))) - .toMatchInlineSnapshot(` - Array [ - "Array must be of length <= 1.", + "string must have length >= 1", ] `); - }); + }); - it('displays field name error message when lower boundary fails', () => { - expect(PathReporter.report(limitedArraySchema(NonEmptyString, 1, 1, 'foobar').decode([]))) - .toMatchInlineSnapshot(` + it('fails when given an empty array', () => { + expect(PathReporter.report(limitedArraySchema(NonEmptyString, 1, 1, fieldName).decode([]))) + .toMatchInlineSnapshot(` Array [ "The length of the field foobar is too short. Array must be of length >= 1.", ] `); - }); + }); - it('displays field name error message when upper boundary fails', () => { - expect( - PathReporter.report(limitedArraySchema(NonEmptyString, 1, 1, 'foobar').decode(['a', 'b'])) - ).toMatchInlineSnapshot(` + it('fails when given an array larger than the limit of one item', () => { + expect( + PathReporter.report(limitedArraySchema(NonEmptyString, 1, 1, fieldName).decode(['a', 'b'])) + ).toMatchInlineSnapshot(` Array [ "The length of the field foobar is too long. Array must be of length <= 1.", ] `); - }); + }); - it('succeeds when given an array of 1 item with a non-empty string', () => { - expect(PathReporter.report(limitedArraySchema(NonEmptyString, 1, 1).decode(['a']))) - .toMatchInlineSnapshot(` + it('succeeds when given an array of 1 item with a non-empty string', () => { + expect(PathReporter.report(limitedArraySchema(NonEmptyString, 1, 1, fieldName).decode(['a']))) + .toMatchInlineSnapshot(` Array [ "No errors!", ] `); - }); + }); - it('succeeds when given an array of 0 item with a non-empty string when the min is 0', () => { - expect(PathReporter.report(limitedArraySchema(NonEmptyString, 0, 2).decode([]))) - .toMatchInlineSnapshot(` + it('succeeds when given an array of 0 item with a non-empty string when the min is 0', () => { + expect(PathReporter.report(limitedArraySchema(NonEmptyString, 0, 2, fieldName).decode([]))) + .toMatchInlineSnapshot(` Array [ "No errors!", ] `); + }); + }); + + describe('limitedStringSchema', () => { + const fieldName = 'foo'; + + it('fails when given string is shorter than minimum', () => { + expect(PathReporter.report(limitedStringSchema(fieldName, 2, 1).decode('a'))) + .toMatchInlineSnapshot(` + Array [ + "The length of the ${fieldName} is too short. The minimum length is 2.", + ] + `); + }); + + it('fails when given string is empty and minimum is not 0', () => { + expect(PathReporter.report(limitedStringSchema(fieldName, 1, 1).decode(''))) + .toMatchInlineSnapshot(` + Array [ + "The ${fieldName} field cannot be an empty string.", + ] + `); + }); + + it('fails when given string consists only empty characters and minimum is not 0', () => { + expect(PathReporter.report(limitedStringSchema(fieldName, 1, 1).decode(' '))) + .toMatchInlineSnapshot(` + Array [ + "The ${fieldName} field cannot be an empty string.", + ] + `); + }); + + it('fails when given string is larger than maximum', () => { + expect(PathReporter.report(limitedStringSchema(fieldName, 1, 5).decode('Hello there!!'))) + .toMatchInlineSnapshot(` + Array [ + "The length of the ${fieldName} is too long. The maximum length is 5.", + ] + `); + }); + + it('succeeds when given string within limit', () => { + expect(PathReporter.report(limitedStringSchema(fieldName, 1, 50).decode('Hello!!'))) + .toMatchInlineSnapshot(` + Array [ + "No errors!", + ] + `); + }); + + it('succeeds when given string is empty and minimum is 0', () => { + expect(PathReporter.report(limitedStringSchema(fieldName, 0, 5).decode(''))) + .toMatchInlineSnapshot(` + Array [ + "No errors!", + ] + `); + }); + + it('succeeds when given string consists only empty characters and minimum is 0', () => { + expect(PathReporter.report(limitedStringSchema(fieldName, 0, 5).decode(' '))) + .toMatchInlineSnapshot(` + Array [ + "No errors!", + ] + `); + }); + + it('succeeds when given string is same as maximum', () => { + expect(PathReporter.report(limitedStringSchema(fieldName, 0, 5).decode('Hello'))) + .toMatchInlineSnapshot(` + Array [ + "No errors!", + ] + `); + }); + + it('succeeds when given string is larger than maximum but same as maximum after trim', () => { + expect(PathReporter.report(limitedStringSchema(fieldName, 0, 5).decode('Hello '))) + .toMatchInlineSnapshot(` + Array [ + "No errors!", + ] + `); + }); }); }); diff --git a/x-pack/plugins/cases/common/schema/index.ts b/x-pack/plugins/cases/common/schema/index.ts index 5e430665b3750..1f8e4e05c4933 100644 --- a/x-pack/plugins/cases/common/schema/index.ts +++ b/x-pack/plugins/cases/common/schema/index.ts @@ -22,11 +22,44 @@ export const NonEmptyString = new rt.Type( rt.identity ); +export const limitedStringSchema = (fieldName: string, min: number, max: number) => + new rt.Type( + 'LimitedString', + rt.string.is, + (input, context) => + either.chain(rt.string.validate(input, context), (s) => { + const trimmedString = s.trim(); + + if (trimmedString.length === 0 && trimmedString.length < min) { + return rt.failure(input, context, `The ${fieldName} field cannot be an empty string.`); + } + + if (trimmedString.length < min) { + return rt.failure( + input, + context, + `The length of the ${fieldName} is too short. The minimum length is ${min}.` + ); + } + + if (trimmedString.length > max) { + return rt.failure( + input, + context, + `The length of the ${fieldName} is too long. The maximum length is ${max}.` + ); + } + + return rt.success(s); + }), + rt.identity + ); + export const limitedArraySchema = ( codec: T, min: number, max: number, - fieldName?: string + fieldName: string ) => new rt.Type>, Array>, unknown>( 'LimitedArray', @@ -34,23 +67,18 @@ export const limitedArraySchema = ( (input, context) => either.chain(rt.array(codec).validate(input, context), (s) => { if (s.length < min) { - const fieldNameErrorMessage = - fieldName != null ? `The length of the field ${fieldName} is too short. ` : ''; - return rt.failure( input, context, - `${fieldNameErrorMessage}Array must be of length >= ${min}.` + `The length of the field ${fieldName} is too short. Array must be of length >= ${min}.` ); } if (s.length > max) { - const fieldNameErrorMessage = - fieldName != null ? `The length of the field ${fieldName} is too long. ` : ''; return rt.failure( input, context, - `${fieldNameErrorMessage}Array must be of length <= ${max}.` + `The length of the field ${fieldName} is too long. Array must be of length <= ${max}.` ); } diff --git a/x-pack/plugins/cases/common/utils/validators.test.ts b/x-pack/plugins/cases/common/utils/validators.test.ts index 78675a18422ff..e05e9223387b6 100644 --- a/x-pack/plugins/cases/common/utils/validators.test.ts +++ b/x-pack/plugins/cases/common/utils/validators.test.ts @@ -5,13 +5,8 @@ * 2.0. */ -import { MAX_ASSIGNEES_PER_CASE, MAX_CATEGORY_LENGTH } from '../constants'; -import { - isInvalidTag, - areTotalAssigneesInvalid, - isCategoryFieldInvalidString, - isCategoryFieldTooLong, -} from './validators'; +import { MAX_ASSIGNEES_PER_CASE } from '../constants'; +import { isInvalidTag, areTotalAssigneesInvalid } from './validators'; describe('validators', () => { describe('isInvalidTag', () => { @@ -55,46 +50,4 @@ describe('validators', () => { expect(areTotalAssigneesInvalid(generateAssignees(MAX_ASSIGNEES_PER_CASE + 1))).toBe(true); }); }); - - describe('isCategoryFieldInvalidString', () => { - it('validates undefined categories correctly', () => { - expect(isCategoryFieldInvalidString()).toBe(false); - }); - - it('validates null categories correctly', () => { - expect(isCategoryFieldInvalidString(null)).toBe(false); - }); - - it('returns false if the category is a non-empty string', () => { - expect(isCategoryFieldInvalidString('foobar')).toBe(false); - }); - - it('returns true if the category is an empty string', () => { - expect(isCategoryFieldInvalidString('')).toBe(true); - }); - - it('returns true if the string contains only spaces', () => { - expect(isCategoryFieldInvalidString(' ')).toBe(true); - }); - }); - - describe('isCategoryFieldTooLong', () => { - it('validates undefined categories correctly', () => { - expect(isCategoryFieldTooLong()).toBe(false); - }); - - it('validates null categories correctly', () => { - expect(isCategoryFieldTooLong(null)).toBe(false); - }); - - it(`returns false if the category is smaller than ${MAX_CATEGORY_LENGTH}`, () => { - expect(isCategoryFieldTooLong('foobar')).toBe(false); - }); - - it(`returns true if the category is longer than ${MAX_CATEGORY_LENGTH}`, () => { - expect(isCategoryFieldTooLong('A very long category with more than fifty characters!')).toBe( - true - ); - }); - }); }); diff --git a/x-pack/plugins/cases/common/utils/validators.ts b/x-pack/plugins/cases/common/utils/validators.ts index 98880e3b445a9..2358ed0a9c2c3 100644 --- a/x-pack/plugins/cases/common/utils/validators.ts +++ b/x-pack/plugins/cases/common/utils/validators.ts @@ -6,7 +6,7 @@ */ import type { CaseAssignees } from '../api'; -import { MAX_ASSIGNEES_PER_CASE, MAX_CATEGORY_LENGTH } from '../constants'; +import { MAX_ASSIGNEES_PER_CASE } from '../constants'; export const isInvalidTag = (value: string) => value.trim() === ''; @@ -17,9 +17,3 @@ export const areTotalAssigneesInvalid = (assignees?: CaseAssignees): boolean => return assignees.length > MAX_ASSIGNEES_PER_CASE; }; - -export const isCategoryFieldInvalidString = (category?: string | null): boolean => - category?.trim().length === 0; - -export const isCategoryFieldTooLong = (category?: string | null): boolean => - category != null && category.length > MAX_CATEGORY_LENGTH; diff --git a/x-pack/plugins/cases/docs/openapi/bundled.json b/x-pack/plugins/cases/docs/openapi/bundled.json index 2ce8031bc1cb9..4d6905006c08d 100644 --- a/x-pack/plugins/cases/docs/openapi/bundled.json +++ b/x-pack/plugins/cases/docs/openapi/bundled.json @@ -12,18 +12,26 @@ "url": "https://www.elastic.co/licensing/elastic-license" } }, - "tags": [ - { - "name": "cases", - "description": "Case APIs enable you to open and track issues." - } - ], "servers": [ { "url": "http://localhost:5601", "description": "local" } ], + "security": [ + { + "basicAuth": [] + }, + { + "apiKeyAuth": [] + } + ], + "tags": [ + { + "name": "cases", + "description": "Case APIs enable you to open and track issues." + } + ], "paths": { "/api/cases": { "post": { @@ -3809,7 +3817,12 @@ "in": "query", "required": true, "schema": { - "type": "string" + "type": "array", + "items": { + "type": "string", + "minItems": 1, + "maxItems": 100 + } }, "example": "d4e7abb0-b462-11ec-9a8d-698504725a43" }, @@ -4604,7 +4617,8 @@ }, "description": { "description": "The description for the case.", - "type": "string" + "type": "string", + "maxLength": 30000 }, "owner": { "$ref": "#/components/schemas/owners" @@ -4618,13 +4632,21 @@ "tags": { "description": "The words and phrases that help categorize cases. It can be an empty array.", "type": "array", + "maxItems": 200, "items": { - "type": "string" + "type": "string", + "maxLength": 256 } }, + "category": { + "description": "Category for the case. It could be a word or a phrase to categorize the case.", + "type": "string", + "maxLength": 50 + }, "title": { "description": "A title for the case.", - "type": "string" + "type": "string", + "maxLength": 160 } } }, @@ -5232,7 +5254,8 @@ }, "id": { "description": "The identifier for the case.", - "type": "string" + "type": "string", + "maxLength": 30000 }, "settings": { "$ref": "#/components/schemas/settings" @@ -5246,13 +5269,21 @@ "tags": { "description": "The words and phrases that help categorize cases.", "type": "array", + "maxItems": 200, "items": { - "type": "string" + "type": "string", + "maxLength": 256 } }, + "category": { + "description": "Category for the case. It could be a word or a phrase to categorize the case.", + "type": "string", + "maxLength": 50 + }, "title": { "description": "A title for the case.", - "type": "string" + "type": "string", + "maxLength": 160 }, "version": { "description": "The current version of the case. To determine this value, use the get case or find cases APIs.", @@ -7130,13 +7161,5 @@ ] } } - }, - "security": [ - { - "basicAuth": [] - }, - { - "apiKeyAuth": [] - } - ] + } } \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/bundled.yaml b/x-pack/plugins/cases/docs/openapi/bundled.yaml index 50184a7b96742..7608a44f3e45e 100644 --- a/x-pack/plugins/cases/docs/openapi/bundled.yaml +++ b/x-pack/plugins/cases/docs/openapi/bundled.yaml @@ -8,12 +8,15 @@ info: license: name: Elastic License 2.0 url: https://www.elastic.co/licensing/elastic-license -tags: - - name: cases - description: Case APIs enable you to open and track issues. servers: - url: http://localhost:5601 description: local +security: + - basicAuth: [] + - apiKeyAuth: [] +tags: + - name: cases + description: Case APIs enable you to open and track issues. paths: /api/cases: post: @@ -2310,7 +2313,11 @@ components: in: query required: true schema: - type: string + type: array + items: + type: string + minItems: 1 + maxItems: 100 example: d4e7abb0-b462-11ec-9a8d-698504725a43 assignees: in: query @@ -2903,6 +2910,7 @@ components: description: description: The description for the case. type: string + maxLength: 30000 owner: $ref: '#/components/schemas/owners' settings: @@ -2912,11 +2920,18 @@ components: tags: description: The words and phrases that help categorize cases. It can be an empty array. type: array + maxItems: 200 items: type: string + maxLength: 256 + category: + description: Category for the case. It could be a word or a phrase to categorize the case. + type: string + maxLength: 50 title: description: A title for the case. type: string + maxLength: 160 case_response_closed_by_properties: title: Case response properties for closed_by type: object @@ -3354,6 +3369,7 @@ components: id: description: The identifier for the case. type: string + maxLength: 30000 settings: $ref: '#/components/schemas/settings' severity: @@ -3363,11 +3379,18 @@ components: tags: description: The words and phrases that help categorize cases. type: array + maxItems: 200 items: type: string + maxLength: 256 + category: + description: Category for the case. It could be a word or a phrase to categorize the case. + type: string + maxLength: 50 title: description: A title for the case. type: string + maxLength: 160 version: description: The current version of the case. To determine this value, use the get case or find cases APIs. type: string @@ -4749,6 +4772,3 @@ components: isPreconfigured: false isDeprecated: false referencedByCount: 0 -security: - - basicAuth: [] - - apiKeyAuth: [] diff --git a/x-pack/plugins/cases/docs/openapi/components/parameters/ids.yaml b/x-pack/plugins/cases/docs/openapi/components/parameters/ids.yaml index 1690b5e6038a0..c84ec64ab2a53 100644 --- a/x-pack/plugins/cases/docs/openapi/components/parameters/ids.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/parameters/ids.yaml @@ -5,8 +5,9 @@ description: > in: query required: true schema: - type: string -example: d4e7abb0-b462-11ec-9a8d-698504725a43 - - - \ No newline at end of file + type: array + items: + type: string + minItems: 1 + maxItems: 100 +example: d4e7abb0-b462-11ec-9a8d-698504725a43 diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/create_case_request.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/create_case_request.yaml index 9f6b039fb2563..1092975985dd0 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/create_case_request.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/create_case_request.yaml @@ -24,6 +24,7 @@ properties: description: description: The description for the case. type: string + maxLength: 30000 owner: $ref: 'owners.yaml' settings: @@ -33,8 +34,15 @@ properties: tags: description: The words and phrases that help categorize cases. It can be an empty array. type: array + maxItems: 200 items: type: string + maxLength: 256 + category: + description: Category for the case. It could be a word or a phrase to categorize the case. + type: string + maxLength: 50 title: description: A title for the case. - type: string \ No newline at end of file + type: string + maxLength: 160 \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/components/schemas/update_case_request.yaml b/x-pack/plugins/cases/docs/openapi/components/schemas/update_case_request.yaml index 35170dc4ef3eb..bd630aec9ca5a 100644 --- a/x-pack/plugins/cases/docs/openapi/components/schemas/update_case_request.yaml +++ b/x-pack/plugins/cases/docs/openapi/components/schemas/update_case_request.yaml @@ -31,6 +31,7 @@ properties: id: description: The identifier for the case. type: string + maxLength: 30000 settings: $ref: 'settings.yaml' severity: @@ -40,11 +41,18 @@ properties: tags: description: The words and phrases that help categorize cases. type: array + maxItems: 200 items: type: string + maxLength: 256 + category: + description: Category for the case. It could be a word or a phrase to categorize the case. + type: string + maxLength: 50 title: description: A title for the case. type: string + maxLength: 160 version: description: The current version of the case. To determine this value, use the get case or find cases APIs. type: string \ No newline at end of file diff --git a/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases.yaml b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases.yaml index ae228f7c1f770..58e73f6a68d17 100644 --- a/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases.yaml +++ b/x-pack/plugins/cases/docs/openapi/paths/s@{spaceid}@api@cases.yaml @@ -23,7 +23,7 @@ post: '200': description: Indicates a successful call. content: - application/json: + application/json: schema: $ref: '../components/schemas/case_response_properties.yaml' examples: @@ -36,7 +36,7 @@ post: schema: $ref: '../components/schemas/4xx_response.yaml' servers: - - url: https://localhost:5601 + - url: https://localhost:5601 delete: summary: Deletes one or more cases. @@ -103,7 +103,7 @@ patch: schema: $ref: '../components/schemas/4xx_response.yaml' servers: - - url: https://localhost:5601 + - url: https://localhost:5601 servers: - - url: https://localhost:5601 \ No newline at end of file + - url: https://localhost:5601 diff --git a/x-pack/plugins/cases/server/client/cases/create.test.ts b/x-pack/plugins/cases/server/client/cases/create.test.ts index 1459eea6f773d..7c57f85336ea9 100644 --- a/x-pack/plugins/cases/server/client/cases/create.test.ts +++ b/x-pack/plugins/cases/server/client/cases/create.test.ts @@ -5,6 +5,12 @@ * 2.0. */ +import { + MAX_DESCRIPTION_LENGTH, + MAX_TAGS_PER_CASE, + MAX_LENGTH_PER_TAG, + MAX_TITLE_LENGTH, +} from '../../../common/constants'; import { SECURITY_SOLUTION_OWNER } from '../../../common'; import { CaseSeverity, ConnectorTypes } from '../../../common/api'; import { mockCases } from '../../mocks'; @@ -94,6 +100,145 @@ describe('create', () => { `"Failed to create case: Error: invalid keys \\"foo\\""` ); }); + }); + + describe('title', () => { + const clientArgs = createCasesClientMockArgs(); + clientArgs.services.caseService.postNewCase.mockResolvedValue(caseSO); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it(`should not throw an error if the title is non empty and less than ${MAX_TITLE_LENGTH} characters`, async () => { + await expect( + create({ ...theCase, title: 'This is a test case!!' }, clientArgs) + ).resolves.not.toThrow(); + }); + + it('should throw an error if the title length is too long', async () => { + await expect( + create( + { + ...theCase, + title: + 'This is a very long title with more than one hundred and sixty characters!! To confirm the maximum limit error thrown for more than one hundred and sixty characters!!', + }, + clientArgs + ) + ).rejects.toThrow( + `Failed to create case: Error: The length of the title is too long. The maximum length is ${MAX_TITLE_LENGTH}.` + ); + }); + + it('should throw an error if the title is an empty string', async () => { + await expect(create({ ...theCase, title: '' }, clientArgs)).rejects.toThrow( + 'Failed to create case: Error: The title field cannot be an empty string.' + ); + }); + + it('should throw an error if the title is a string with empty characters', async () => { + await expect(create({ ...theCase, title: ' ' }, clientArgs)).rejects.toThrow( + 'Failed to create case: Error: The title field cannot be an empty string.' + ); + }); + }); + + describe('description', () => { + const clientArgs = createCasesClientMockArgs(); + clientArgs.services.caseService.postNewCase.mockResolvedValue(caseSO); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it(`should not throw an error if the description is non empty and less than ${MAX_DESCRIPTION_LENGTH} characters`, async () => { + await expect( + create({ ...theCase, description: 'This is a test description!!' }, clientArgs) + ).resolves.not.toThrow(); + }); + + it('should throw an error if the description length is too long', async () => { + const description = Array(MAX_DESCRIPTION_LENGTH + 1) + .fill('x') + .toString(); + + await expect(create({ ...theCase, description }, clientArgs)).rejects.toThrow( + `Failed to create case: Error: The length of the description is too long. The maximum length is ${MAX_DESCRIPTION_LENGTH}.` + ); + }); + + it('should throw an error if the description is an empty string', async () => { + await expect(create({ ...theCase, description: '' }, clientArgs)).rejects.toThrow( + 'Failed to create case: Error: The description field cannot be an empty string.' + ); + }); + + it('should throw an error if the description is a string with empty characters', async () => { + await expect(create({ ...theCase, description: ' ' }, clientArgs)).rejects.toThrow( + 'Failed to create case: Error: The description field cannot be an empty string.' + ); + }); + }); + + describe('tags', () => { + const clientArgs = createCasesClientMockArgs(); + clientArgs.services.caseService.postNewCase.mockResolvedValue(caseSO); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should not throw an error if the tags array is empty', async () => { + await expect(create({ ...theCase, tags: [] }, clientArgs)).resolves.not.toThrow(); + }); + + it('should not throw an error if the tags array has non empty string within limit', async () => { + await expect(create({ ...theCase, tags: ['abc'] }, clientArgs)).resolves.not.toThrow(); + }); + + it('should throw an error if the tags array length is too long', async () => { + const tags = Array(MAX_TAGS_PER_CASE + 1).fill('foo'); + + await expect(create({ ...theCase, tags }, clientArgs)).rejects.toThrow( + `Failed to create case: Error: The length of the field tags is too long. Array must be of length <= ${MAX_TAGS_PER_CASE}.` + ); + }); + + it('should throw an error if the tags array has empty string', async () => { + await expect(create({ ...theCase, tags: [''] }, clientArgs)).rejects.toThrow( + 'Failed to create case: Error: The tag field cannot be an empty string.' + ); + }); + + it('should throw an error if the tags array has string with empty characters', async () => { + await expect(create({ ...theCase, tags: [' '] }, clientArgs)).rejects.toThrow( + 'Failed to create case: Error: The tag field cannot be an empty string.' + ); + }); + + it('should throw an error if the tag length is too long', async () => { + const tag = Array(MAX_LENGTH_PER_TAG + 1) + .fill('f') + .toString(); + + await expect(create({ ...theCase, tags: [tag] }, clientArgs)).rejects.toThrow( + `Failed to create case: Error: The length of the tag is too long. The maximum length is ${MAX_LENGTH_PER_TAG}.` + ); + }); + }); + + describe('Category', () => { + const clientArgs = createCasesClientMockArgs(); + clientArgs.services.caseService.postNewCase.mockResolvedValue(caseSO); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should not throw an error if the category is null', async () => { + await expect(create({ ...theCase, category: null }, clientArgs)).resolves.not.toThrow(); + }); it('should throw an error if the category length is too long', async () => { await expect( @@ -106,13 +251,13 @@ describe('create', () => { it('should throw an error if the category is an empty string', async () => { await expect(create({ ...theCase, category: '' }, clientArgs)).rejects.toThrow( - 'Failed to create case: Error: The category cannot be an empty string.' + 'Failed to create case: Error: The category field cannot be an empty string.,Invalid value "" supplied to "category"' ); }); it('should throw an error if the category is a string with empty characters', async () => { await expect(create({ ...theCase, category: ' ' }, clientArgs)).rejects.toThrow( - 'Failed to create case: Error: The category cannot be an empty string.' + 'Failed to create case: Error: The category field cannot be an empty string.,Invalid value " " supplied to "category"' ); }); }); diff --git a/x-pack/plugins/cases/server/client/cases/create.ts b/x-pack/plugins/cases/server/client/cases/create.ts index d60b1510b56d8..1b787b415c4d0 100644 --- a/x-pack/plugins/cases/server/client/cases/create.ts +++ b/x-pack/plugins/cases/server/client/cases/create.ts @@ -17,17 +17,8 @@ import { CaseSeverity, decodeWithExcessOrThrow, } from '../../../common/api'; -import { - MAX_ASSIGNEES_PER_CASE, - MAX_CATEGORY_LENGTH, - MAX_TITLE_LENGTH, -} from '../../../common/constants'; -import { - isInvalidTag, - areTotalAssigneesInvalid, - isCategoryFieldTooLong, - isCategoryFieldInvalidString, -} from '../../../common/utils/validators'; +import { MAX_ASSIGNEES_PER_CASE } from '../../../common/constants'; +import { areTotalAssigneesInvalid } from '../../../common/utils/validators'; import { Operations } from '../../authorization'; import { createCaseError } from '../../common/error'; @@ -36,18 +27,6 @@ import type { CasesClientArgs } from '..'; import { LICENSING_CASE_ASSIGNMENT_FEATURE } from '../../common/constants'; import { decodeOrThrow } from '../../../common/api/runtime_types'; -function validateCategory(category?: string | null) { - if (isCategoryFieldTooLong(category)) { - throw Boom.badRequest( - `The length of the category is too long. The maximum length is ${MAX_CATEGORY_LENGTH}` - ); - } - - if (isCategoryFieldInvalidString(category)) { - throw Boom.badRequest('The category cannot be an empty string.'); - } -} - /** * Creates a new case. * @@ -64,18 +43,6 @@ export const create = async (data: CasePostRequest, clientArgs: CasesClientArgs) try { const query = decodeWithExcessOrThrow(CasePostRequestRt)(data); - if (query.title.length > MAX_TITLE_LENGTH) { - throw Boom.badRequest( - `The length of the title is too long. The maximum length is ${MAX_TITLE_LENGTH}.` - ); - } - - if (query.tags.some(isInvalidTag)) { - throw Boom.badRequest('A tag must contain at least one non-space character'); - } - - validateCategory(query.category); - const savedObjectID = SavedObjectsUtils.generateId(); await auth.ensureAuthorized({ diff --git a/x-pack/plugins/cases/server/client/cases/delete.test.ts b/x-pack/plugins/cases/server/client/cases/delete.test.ts index ebe64da0c2baf..37be71049cf2d 100644 --- a/x-pack/plugins/cases/server/client/cases/delete.test.ts +++ b/x-pack/plugins/cases/server/client/cases/delete.test.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { MAX_FILES_PER_CASE } from '../../../common/constants'; +import { MAX_DELETE_IDS_LENGTH, MAX_FILES_PER_CASE } from '../../../common/constants'; import type { FindFileArgs } from '@kbn/files-plugin/server'; import { createFileServiceMock } from '@kbn/files-plugin/server/mocks'; import type { FileJSON } from '@kbn/shared-ux-file-types'; @@ -95,6 +95,22 @@ describe('delete', () => { }); }); }); + + describe('errors', () => { + it(`throws 400 when trying to delete more than ${MAX_DELETE_IDS_LENGTH} cases at a time`, async () => { + const caseIds = new Array(MAX_DELETE_IDS_LENGTH + 1).fill('id'); + + await expect(deleteCases(caseIds, clientArgs)).rejects.toThrowError( + 'Error: The length of the field ids is too long. Array must be of length <= 100.' + ); + }); + + it('throws 400 when no id is passed to delete', async () => { + await expect(deleteCases([], clientArgs)).rejects.toThrowError( + 'Error: The length of the field ids is too short. Array must be of length >= 1.' + ); + }); + }); }); }); diff --git a/x-pack/plugins/cases/server/client/cases/delete.ts b/x-pack/plugins/cases/server/client/cases/delete.ts index 5fbefa1b4405b..78fffc19a2edc 100644 --- a/x-pack/plugins/cases/server/client/cases/delete.ts +++ b/x-pack/plugins/cases/server/client/cases/delete.ts @@ -10,6 +10,8 @@ import pMap from 'p-map'; import { chunk } from 'lodash'; import type { SavedObjectsBulkDeleteObject } from '@kbn/core/server'; import type { FileServiceStart } from '@kbn/files-plugin/server'; +import type { CasesDeleteRequest } from '../../../common/api'; +import { CasesDeleteRequestRt, decodeWithExcessOrThrow } from '../../../common/api'; import { CASE_COMMENT_SAVED_OBJECT, CASE_SAVED_OBJECT, @@ -26,7 +28,10 @@ import { createFileEntities, deleteFiles } from '../files'; /** * Deletes the specified cases and their attachments. */ -export async function deleteCases(ids: string[], clientArgs: CasesClientArgs): Promise { +export async function deleteCases( + ids: CasesDeleteRequest, + clientArgs: CasesClientArgs +): Promise { const { services: { caseService, attachmentService, userActionService, alertsService }, logger, @@ -35,7 +40,8 @@ export async function deleteCases(ids: string[], clientArgs: CasesClientArgs): P } = clientArgs; try { - const cases = await caseService.getCases({ caseIds: ids }); + const caseIds = decodeWithExcessOrThrow(CasesDeleteRequestRt)(ids); + const cases = await caseService.getCases({ caseIds }); const entities = new Map(); for (const theCase of cases.saved_objects) { diff --git a/x-pack/plugins/cases/server/client/cases/update.test.ts b/x-pack/plugins/cases/server/client/cases/update.test.ts index 4c90dc6b43304..e789c6d53ca07 100644 --- a/x-pack/plugins/cases/server/client/cases/update.test.ts +++ b/x-pack/plugins/cases/server/client/cases/update.test.ts @@ -5,6 +5,13 @@ * 2.0. */ +import { + MAX_CATEGORY_LENGTH, + MAX_DESCRIPTION_LENGTH, + MAX_TAGS_PER_CASE, + MAX_LENGTH_PER_TAG, + MAX_TITLE_LENGTH, +} from '../../../common/constants'; import { mockCases } from '../../mocks'; import { createCasesClientMockArgs } from '../mocks'; import { update } from './update'; @@ -286,6 +293,27 @@ describe('update', () => { }); }); + it(`does not throw error when category is non empty string less than ${MAX_CATEGORY_LENGTH} characters`, async () => { + clientArgs.services.caseService.patchCases.mockResolvedValue({ + saved_objects: [{ ...mockCases[0] }], + }); + + await expect( + update( + { + cases: [ + { + id: mockCases[0].id, + version: mockCases[0].version ?? '', + category: 'foobar', + }, + ], + }, + clientArgs + ) + ).resolves.not.toThrow(); + }); + it('does not update the category if the length is too long', async () => { await expect( update( @@ -301,11 +329,11 @@ describe('update', () => { clientArgs ) ).rejects.toThrow( - 'Failed to update case, ids: [{"id":"mock-id-1","version":"WzAsMV0="}]: Error: The length of the category is too long. The maximum length is 50, ids: [mock-id-1]' + `Failed to update case, ids: [{"id":"mock-id-1","version":"WzAsMV0="}]: Error: The length of the category is too long. The maximum length is ${MAX_CATEGORY_LENGTH}.,Invalid value \"A very long category with more than fifty characters!\" supplied to \"cases,category\"` ); }); - it('does not update the category if it is just an empty string', async () => { + it('throws error if category is just an empty string', async () => { await expect( update( { @@ -320,11 +348,11 @@ describe('update', () => { clientArgs ) ).rejects.toThrow( - 'Failed to update case, ids: [{"id":"mock-id-1","version":"WzAsMV0="}]: Error: The category cannot be an empty string. Ids: [mock-id-1]' + 'Failed to update case, ids: [{"id":"mock-id-1","version":"WzAsMV0="}]: Error: The category field cannot be an empty string.,Invalid value "" supplied to "cases,category"' ); }); - it('does not update the category if it is a string with empty characters', async () => { + it('throws error if category is a string with empty characters', async () => { await expect( update( { @@ -339,7 +367,337 @@ describe('update', () => { clientArgs ) ).rejects.toThrow( - 'Failed to update case, ids: [{"id":"mock-id-1","version":"WzAsMV0="}]: Error: The category cannot be an empty string. Ids: [mock-id-1]' + 'Failed to update case, ids: [{"id":"mock-id-1","version":"WzAsMV0="}]: Error: The category field cannot be an empty string.,Invalid value " " supplied to "cases,category"' + ); + }); + }); + + describe('Title', () => { + const clientArgs = createCasesClientMockArgs(); + + beforeEach(() => { + jest.clearAllMocks(); + clientArgs.services.caseService.getCases.mockResolvedValue({ saved_objects: mockCases }); + clientArgs.services.caseService.getAllCaseComments.mockResolvedValue({ + saved_objects: [], + total: 0, + per_page: 10, + page: 1, + }); + }); + + it(`does not throw error when title is non empty string less than ${MAX_TITLE_LENGTH} characters`, async () => { + clientArgs.services.caseService.patchCases.mockResolvedValue({ + saved_objects: [{ ...mockCases[0] }], + }); + + await expect( + update( + { + cases: [ + { + id: mockCases[0].id, + version: mockCases[0].version ?? '', + title: 'This is a test case!!', + }, + ], + }, + clientArgs + ) + ).resolves.not.toThrow(); + }); + + it('throws error if the title is too long', async () => { + await expect( + update( + { + cases: [ + { + id: mockCases[0].id, + version: mockCases[0].version ?? '', + title: + 'This is a very long title with more than one hundred and sixty characters!! To confirm the maximum limit error thrown for more than one hundred and sixty characters!!', + }, + ], + }, + clientArgs + ) + ).rejects.toThrow( + `Failed to update case, ids: [{"id":"mock-id-1","version":"WzAsMV0="}]: Error: The length of the title is too long. The maximum length is ${MAX_TITLE_LENGTH}.` + ); + }); + + it('throws error if title is just an empty string', async () => { + await expect( + update( + { + cases: [ + { + id: mockCases[0].id, + version: mockCases[0].version ?? '', + title: '', + }, + ], + }, + clientArgs + ) + ).rejects.toThrow( + 'Failed to update case, ids: [{"id":"mock-id-1","version":"WzAsMV0="}]: Error: The title field cannot be an empty string.' + ); + }); + + it('throws error if title is a string with empty characters', async () => { + await expect( + update( + { + cases: [ + { + id: mockCases[0].id, + version: mockCases[0].version ?? '', + title: ' ', + }, + ], + }, + clientArgs + ) + ).rejects.toThrow( + 'Failed to update case, ids: [{"id":"mock-id-1","version":"WzAsMV0="}]: Error: The title field cannot be an empty string.' + ); + }); + }); + + describe('Description', () => { + const clientArgs = createCasesClientMockArgs(); + + beforeEach(() => { + jest.clearAllMocks(); + clientArgs.services.caseService.getCases.mockResolvedValue({ saved_objects: mockCases }); + clientArgs.services.caseService.getAllCaseComments.mockResolvedValue({ + saved_objects: [], + total: 0, + per_page: 10, + page: 1, + }); + }); + + it(`does not throw error when description is non empty string less than ${MAX_DESCRIPTION_LENGTH} characters`, async () => { + clientArgs.services.caseService.patchCases.mockResolvedValue({ + saved_objects: [{ ...mockCases[0] }], + }); + + await expect( + update( + { + cases: [ + { + id: mockCases[0].id, + version: mockCases[0].version ?? '', + description: 'New updated description!!', + }, + ], + }, + clientArgs + ) + ).resolves.not.toThrow(); + }); + + it('throws error when the description is too long', async () => { + const description = Array(MAX_DESCRIPTION_LENGTH + 1) + .fill('a') + .toString(); + + await expect( + update( + { + cases: [ + { + id: mockCases[0].id, + version: mockCases[0].version ?? '', + description, + }, + ], + }, + clientArgs + ) + ).rejects.toThrow( + `Failed to update case, ids: [{"id":"mock-id-1","version":"WzAsMV0="}]: Error: The length of the description is too long. The maximum length is ${MAX_DESCRIPTION_LENGTH}.` + ); + }); + + it('throws error if description is just an empty string', async () => { + await expect( + update( + { + cases: [ + { + id: mockCases[0].id, + version: mockCases[0].version ?? '', + description: '', + }, + ], + }, + clientArgs + ) + ).rejects.toThrow( + 'Failed to update case, ids: [{"id":"mock-id-1","version":"WzAsMV0="}]: Error: The description field cannot be an empty string.' + ); + }); + + it('throws error if description is a string with empty characters', async () => { + await expect( + update( + { + cases: [ + { + id: mockCases[0].id, + version: mockCases[0].version ?? '', + description: ' ', + }, + ], + }, + clientArgs + ) + ).rejects.toThrow( + 'Failed to update case, ids: [{"id":"mock-id-1","version":"WzAsMV0="}]: Error: The description field cannot be an empty string.' + ); + }); + }); + + describe('Tags', () => { + const clientArgs = createCasesClientMockArgs(); + + beforeEach(() => { + jest.clearAllMocks(); + clientArgs.services.caseService.getCases.mockResolvedValue({ saved_objects: mockCases }); + clientArgs.services.caseService.getAllCaseComments.mockResolvedValue({ + saved_objects: [], + total: 0, + per_page: 10, + page: 1, + }); + }); + + it('does not throw error when tags array is empty', async () => { + clientArgs.services.caseService.patchCases.mockResolvedValue({ + saved_objects: [{ ...mockCases[0] }], + }); + + await expect( + update( + { + cases: [ + { + id: mockCases[0].id, + version: mockCases[0].version ?? '', + tags: [], + }, + ], + }, + clientArgs + ) + ).resolves.not.toThrow(); + }); + + it(`does not throw error when tags array length is less than ${MAX_TAGS_PER_CASE} and tag has ${MAX_LENGTH_PER_TAG} characters`, async () => { + clientArgs.services.caseService.patchCases.mockResolvedValue({ + saved_objects: [{ ...mockCases[0] }], + }); + + await expect( + update( + { + cases: [ + { + id: mockCases[0].id, + version: mockCases[0].version ?? '', + tags: ['foo', 'bar'], + }, + ], + }, + clientArgs + ) + ).resolves.not.toThrow(); + }); + + it('throws error if the tags array length is too long', async () => { + const tags = Array(MAX_TAGS_PER_CASE + 1).fill('foo'); + + await expect( + update( + { + cases: [ + { + id: mockCases[0].id, + version: mockCases[0].version ?? '', + tags, + }, + ], + }, + clientArgs + ) + ).rejects.toThrow( + `Failed to update case, ids: [{"id":"mock-id-1","version":"WzAsMV0="}]: Error: The length of the field tags is too long. Array must be of length <= ${MAX_TAGS_PER_CASE}.` + ); + }); + + it('throws error if the tag length is too long', async () => { + const tag = Array(MAX_LENGTH_PER_TAG + 1) + .fill('f') + .toString(); + + await expect( + update( + { + cases: [ + { + id: mockCases[0].id, + version: mockCases[0].version ?? '', + tags: [tag], + }, + ], + }, + clientArgs + ) + ).rejects.toThrow( + `Failed to update case, ids: [{"id":"mock-id-1","version":"WzAsMV0="}]: Error: The length of the tag is too long. The maximum length is ${MAX_LENGTH_PER_TAG}.` + ); + }); + + it('throws error if tag is empty string', async () => { + await expect( + update( + { + cases: [ + { + id: mockCases[0].id, + version: mockCases[0].version ?? '', + tags: [''], + }, + ], + }, + clientArgs + ) + ).rejects.toThrow( + 'Failed to update case, ids: [{"id":"mock-id-1","version":"WzAsMV0="}]: Error: The tag field cannot be an empty string.' + ); + }); + + it('throws error if tag is a string with empty characters', async () => { + await expect( + update( + { + cases: [ + { + id: mockCases[0].id, + version: mockCases[0].version ?? '', + tags: [' '], + }, + ], + }, + clientArgs + ) + ).rejects.toThrow( + 'Failed to update case, ids: [{"id":"mock-id-1","version":"WzAsMV0="}]: Error: The tag field cannot be an empty string.' ); }); }); diff --git a/x-pack/plugins/cases/server/client/cases/update.ts b/x-pack/plugins/cases/server/client/cases/update.ts index e415b77e2eb4b..a685f3c2a2fc7 100644 --- a/x-pack/plugins/cases/server/client/cases/update.ts +++ b/x-pack/plugins/cases/server/client/cases/update.ts @@ -17,11 +17,7 @@ import type { import { nodeBuilder } from '@kbn/es-query'; -import { - areTotalAssigneesInvalid, - isCategoryFieldInvalidString, - isCategoryFieldTooLong, -} from '../../../common/utils/validators'; +import { areTotalAssigneesInvalid } from '../../../common/utils/validators'; import type { CaseAssignees, CaseAttributes, @@ -43,8 +39,6 @@ import { CASE_COMMENT_SAVED_OBJECT, CASE_SAVED_OBJECT, MAX_ASSIGNEES_PER_CASE, - MAX_CATEGORY_LENGTH, - MAX_TITLE_LENGTH, } from '../../../common/constants'; import { arraysDifference, getCaseToUpdate } from '../utils'; @@ -78,24 +72,6 @@ function throwIfUpdateOwner(requests: UpdateRequestWithOriginalCase[]) { } } -/** - * Throws an error if any of the requests updates a title and the length is over MAX_TITLE_LENGTH. - */ -function throwIfTitleIsInvalid(requests: UpdateRequestWithOriginalCase[]) { - const requestsInvalidTitle = requests.filter( - ({ updateReq }) => updateReq.title !== undefined && updateReq.title.length > MAX_TITLE_LENGTH - ); - - if (requestsInvalidTitle.length > 0) { - const ids = requestsInvalidTitle.map(({ updateReq }) => updateReq.id); - throw Boom.badRequest( - `The length of the title is too long. The maximum length is ${MAX_TITLE_LENGTH}, ids: [${ids.join( - ', ' - )}]` - ); - } -} - /** * Throws an error if any of the requests attempt to update the assignees of the case * without the appropriate license @@ -122,40 +98,6 @@ function throwIfUpdateAssigneesWithoutValidLicense( } } -/** - * Throws an error if any of the requests tries to update the category and - * the length is > MAX_CATEGORY_LENGTH. - */ -function throwIfCategoryLengthIsInvalid(requests: UpdateRequestWithOriginalCase[]) { - const requestsInvalidCategory = requests.filter(({ updateReq }) => - isCategoryFieldTooLong(updateReq.category) - ); - - if (requestsInvalidCategory.length > 0) { - const ids = requestsInvalidCategory.map(({ updateReq }) => updateReq.id); - throw Boom.badRequest( - `The length of the category is too long. The maximum length is ${MAX_CATEGORY_LENGTH}, ids: [${ids.join( - ', ' - )}]` - ); - } -} - -/** - * Throws an error if any of the requests tries to update the category and - * the new value is an empty string. - */ -function throwIfCategoryIsInvalidString(requests: UpdateRequestWithOriginalCase[]) { - const requestsInvalidCategory = requests.filter(({ updateReq }) => - isCategoryFieldInvalidString(updateReq.category) - ); - - if (requestsInvalidCategory.length > 0) { - const ids = requestsInvalidCategory.map(({ updateReq }) => updateReq.id); - throw Boom.badRequest(`The category cannot be an empty string. Ids: [${ids.join(', ')}]`); - } -} - function notifyPlatinumUsage( licensingService: LicensingService, requests: UpdateRequestWithOriginalCase[] @@ -424,9 +366,6 @@ export const update = async ( const hasPlatinumLicense = await licensingService.isAtLeastPlatinum(); throwIfUpdateOwner(casesToUpdate); - throwIfTitleIsInvalid(casesToUpdate); - throwIfCategoryLengthIsInvalid(casesToUpdate); - throwIfCategoryIsInvalidString(casesToUpdate); throwIfUpdateAssigneesWithoutValidLicense(casesToUpdate, hasPlatinumLicense); throwIfTotalAssigneesAreInvalid(casesToUpdate); diff --git a/x-pack/plugins/cloud/public/mocks.tsx b/x-pack/plugins/cloud/public/mocks.tsx index 38c63ecb5cfb8..187a8010dc547 100644 --- a/x-pack/plugins/cloud/public/mocks.tsx +++ b/x-pack/plugins/cloud/public/mocks.tsx @@ -39,6 +39,7 @@ const createStartMock = (): jest.Mocked => ({ cloudId: 'mock-cloud-id', isCloudEnabled: true, deploymentUrl: 'deployment-url', + billingUrl: 'billing-url', profileUrl: 'profile-url', organizationUrl: 'organization-url', }); diff --git a/x-pack/plugins/cloud/public/plugin.tsx b/x-pack/plugins/cloud/public/plugin.tsx index 02c2c6d51d5ca..78bd6a8a9ef9a 100644 --- a/x-pack/plugins/cloud/public/plugin.tsx +++ b/x-pack/plugins/cloud/public/plugin.tsx @@ -22,6 +22,7 @@ export interface CloudConfigType { base_url?: string; profile_url?: string; deployment_url?: string; + billing_url?: string; organization_url?: string; trial_end_date?: string; is_elastic_staff_owned?: boolean; @@ -30,6 +31,7 @@ export interface CloudConfigType { interface CloudUrls { deploymentUrl?: string; profileUrl?: string; + billingUrl?: string; organizationUrl?: string; snapshotsUrl?: string; } @@ -99,12 +101,13 @@ export class CloudPlugin implements Plugin { ); }; - const { deploymentUrl, profileUrl, organizationUrl } = this.getCloudUrls(); + const { deploymentUrl, profileUrl, billingUrl, organizationUrl } = this.getCloudUrls(); return { CloudContextProvider, isCloudEnabled: this.isCloudEnabled, cloudId: this.config.id, + billingUrl, deploymentUrl, profileUrl, organizationUrl, @@ -116,6 +119,7 @@ export class CloudPlugin implements Plugin { private getCloudUrls(): CloudUrls { const { profile_url: profileUrl, + billing_url: billingUrl, organization_url: organizationUrl, deployment_url: deploymentUrl, base_url: baseUrl, @@ -123,12 +127,14 @@ export class CloudPlugin implements Plugin { const fullCloudDeploymentUrl = getFullCloudUrl(baseUrl, deploymentUrl); const fullCloudProfileUrl = getFullCloudUrl(baseUrl, profileUrl); + const fullCloudBillingUrl = getFullCloudUrl(baseUrl, billingUrl); const fullCloudOrganizationUrl = getFullCloudUrl(baseUrl, organizationUrl); const fullCloudSnapshotsUrl = `${fullCloudDeploymentUrl}/${CLOUD_SNAPSHOTS_PATH}`; return { deploymentUrl: fullCloudDeploymentUrl, profileUrl: fullCloudProfileUrl, + billingUrl: fullCloudBillingUrl, organizationUrl: fullCloudOrganizationUrl, snapshotsUrl: fullCloudSnapshotsUrl, }; diff --git a/x-pack/plugins/cloud/public/types.ts b/x-pack/plugins/cloud/public/types.ts index ae862a981cd55..a482729fb7ac7 100644 --- a/x-pack/plugins/cloud/public/types.ts +++ b/x-pack/plugins/cloud/public/types.ts @@ -28,6 +28,10 @@ export interface CloudStart { * The full URL to the user profile page on Elastic Cloud. Undefined if not running on Cloud. */ profileUrl?: string; + /** + * The full URL to the billing page on Elastic Cloud. Undefined if not running on Cloud. + */ + billingUrl?: string; /** * The full URL to the organization management page on Elastic Cloud. Undefined if not running on Cloud. */ diff --git a/x-pack/plugins/cloud/server/config.ts b/x-pack/plugins/cloud/server/config.ts index 028298c2da331..7420b2b13b36b 100644 --- a/x-pack/plugins/cloud/server/config.ts +++ b/x-pack/plugins/cloud/server/config.ts @@ -24,6 +24,7 @@ const configSchema = schema.object({ cname: schema.maybe(schema.string()), deployment_url: schema.maybe(schema.string()), id: schema.maybe(schema.string()), + billing_url: schema.maybe(schema.string()), organization_url: schema.maybe(schema.string()), profile_url: schema.maybe(schema.string()), trial_end_date: schema.maybe(schema.string()), @@ -38,6 +39,7 @@ export const config: PluginConfigDescriptor = { cname: true, deployment_url: true, id: true, + billing_url: true, organization_url: true, profile_url: true, trial_end_date: true, diff --git a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/help_menu_links.ts b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/help_menu_links.ts new file mode 100644 index 0000000000000..82b0e86e6569a --- /dev/null +++ b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/help_menu_links.ts @@ -0,0 +1,40 @@ +/* + * Copyright 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 { ChromeHelpMenuLink } from '@kbn/core-chrome-browser'; +import type { DocLinksStart } from '@kbn/core-doc-links-browser'; + +export const createHelpMenuLinks = ({ + docLinks, + helpSupportUrl, +}: { + docLinks: DocLinksStart; + helpSupportUrl: string; +}) => { + const helpMenuLinks: ChromeHelpMenuLink[] = [ + { + title: i18n.translate('xpack.cloudLinks.helpMenuLinks.documentation', { + defaultMessage: 'Documentation', + }), + href: docLinks.links.elasticStackGetStarted, + }, + { + title: i18n.translate('xpack.cloudLinks.helpMenuLinks.support', { + defaultMessage: 'Support', + }), + href: helpSupportUrl, + }, + { + title: i18n.translate('xpack.cloudLinks.helpMenuLinks.giveFeedback', { + defaultMessage: 'Give feedback', + }), + href: docLinks.links.kibana.feedback, + }, + ]; + + return helpMenuLinks; +}; diff --git a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/maybe_add_cloud_links.test.ts b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/maybe_add_cloud_links.test.ts index 4bc8edd057b6e..16d29325502e1 100644 --- a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/maybe_add_cloud_links.test.ts +++ b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/maybe_add_cloud_links.test.ts @@ -18,6 +18,7 @@ describe('maybeAddCloudLinks', () => { security, chrome: coreMock.createStart().chrome, cloud: { ...cloudMock.createStart(), isCloudEnabled: false }, + docLinks: coreMock.createStart().docLinks, }); // Since there's a promise, let's wait for the next tick await new Promise((resolve) => process.nextTick(resolve)); @@ -29,11 +30,12 @@ describe('maybeAddCloudLinks', () => { security.authc.getCurrentUser.mockResolvedValue( securityMock.createMockAuthenticatedUser({ elastic_cloud_user: true }) ); - const chrome = coreMock.createStart().chrome; + const { chrome, docLinks } = coreMock.createStart(); maybeAddCloudLinks({ security, chrome, cloud: { ...cloudMock.createStart(), isCloudEnabled: true }, + docLinks, }); // Since there's a promise, let's wait for the next tick await new Promise((resolve) => process.nextTick(resolve)); @@ -55,15 +57,41 @@ describe('maybeAddCloudLinks', () => { Object { "href": "profile-url", "iconType": "user", - "label": "Edit profile", + "label": "Profile", "order": 100, "setAsProfile": true, }, + Object { + "href": "billing-url", + "iconType": "visGauge", + "label": "Billing", + "order": 200, + }, Object { "href": "organization-url", "iconType": "gear", - "label": "Account & Billing", - "order": 200, + "label": "Organization", + "order": 300, + }, + ], + ] + `); + + expect(chrome.setHelpMenuLinks).toHaveBeenCalledTimes(1); + expect(chrome.setHelpMenuLinks.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "href": "https://www.elastic.co/guide/en/index.html", + "title": "Documentation", + }, + Object { + "href": "https://www.elastic.co/support", + "title": "Support", + }, + Object { + "href": "https://www.elastic.co/products/kibana/feedback?blade=kibanafeedback", + "title": "Give feedback", }, ], ] @@ -73,11 +101,12 @@ describe('maybeAddCloudLinks', () => { it('when cloud enabled and it fails to fetch the user, it sets the links', async () => { const security = securityMock.createStart(); security.authc.getCurrentUser.mockRejectedValue(new Error('Something went terribly wrong')); - const chrome = coreMock.createStart().chrome; + const { chrome, docLinks } = coreMock.createStart(); maybeAddCloudLinks({ security, chrome, cloud: { ...cloudMock.createStart(), isCloudEnabled: true }, + docLinks, }); // Since there's a promise, let's wait for the next tick await new Promise((resolve) => process.nextTick(resolve)); @@ -99,15 +128,40 @@ describe('maybeAddCloudLinks', () => { Object { "href": "profile-url", "iconType": "user", - "label": "Edit profile", + "label": "Profile", "order": 100, "setAsProfile": true, }, + Object { + "href": "billing-url", + "iconType": "visGauge", + "label": "Billing", + "order": 200, + }, Object { "href": "organization-url", "iconType": "gear", - "label": "Account & Billing", - "order": 200, + "label": "Organization", + "order": 300, + }, + ], + ] + `); + expect(chrome.setHelpMenuLinks).toHaveBeenCalledTimes(1); + expect(chrome.setHelpMenuLinks.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "href": "https://www.elastic.co/guide/en/index.html", + "title": "Documentation", + }, + Object { + "href": "https://www.elastic.co/support", + "title": "Support", + }, + Object { + "href": "https://www.elastic.co/products/kibana/feedback?blade=kibanafeedback", + "title": "Give feedback", }, ], ] @@ -119,16 +173,18 @@ describe('maybeAddCloudLinks', () => { security.authc.getCurrentUser.mockResolvedValue( securityMock.createMockAuthenticatedUser({ elastic_cloud_user: false }) ); - const chrome = coreMock.createStart().chrome; + const { chrome, docLinks } = coreMock.createStart(); maybeAddCloudLinks({ security, chrome, cloud: { ...cloudMock.createStart(), isCloudEnabled: true }, + docLinks, }); // Since there's a promise, let's wait for the next tick await new Promise((resolve) => process.nextTick(resolve)); expect(security.authc.getCurrentUser).toHaveBeenCalledTimes(1); expect(chrome.setCustomNavLink).not.toHaveBeenCalled(); expect(security.navControlService.addUserMenuLinks).not.toHaveBeenCalled(); + expect(chrome.setHelpMenuLinks).not.toHaveBeenCalledTimes(1); }); }); diff --git a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/maybe_add_cloud_links.ts b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/maybe_add_cloud_links.ts index 383709e1e7e8c..3d7aa271ed866 100644 --- a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/maybe_add_cloud_links.ts +++ b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/maybe_add_cloud_links.ts @@ -5,44 +5,63 @@ * 2.0. */ -import { catchError, defer, filter, map, of } from 'rxjs'; +import { catchError, defer, filter, map, of, combineLatest } from 'rxjs'; import { i18n } from '@kbn/i18n'; import type { CloudStart } from '@kbn/cloud-plugin/public'; import type { ChromeStart } from '@kbn/core/public'; import type { SecurityPluginStart } from '@kbn/security-plugin/public'; +import type { DocLinksStart } from '@kbn/core-doc-links-browser'; import { createUserMenuLinks } from './user_menu_links'; +import { createHelpMenuLinks } from './help_menu_links'; export interface MaybeAddCloudLinksDeps { security: SecurityPluginStart; chrome: ChromeStart; cloud: CloudStart; + docLinks: DocLinksStart; } -export function maybeAddCloudLinks({ security, chrome, cloud }: MaybeAddCloudLinksDeps): void { +export function maybeAddCloudLinks({ + security, + chrome, + cloud, + docLinks, +}: MaybeAddCloudLinksDeps): void { + const userObservable = defer(() => security.authc.getCurrentUser()).pipe( + // Check if user is a cloud user. + map((user) => user.elastic_cloud_user), + // If user is not defined due to an unexpected error, then fail *open*. + catchError(() => of(true)), + filter((isElasticCloudUser) => isElasticCloudUser === true), + map(() => { + if (cloud.deploymentUrl) { + chrome.setCustomNavLink({ + title: i18n.translate('xpack.cloudLinks.deploymentLinkLabel', { + defaultMessage: 'Manage this deployment', + }), + euiIconType: 'logoCloud', + href: cloud.deploymentUrl, + }); + } + const userMenuLinks = createUserMenuLinks(cloud); + security.navControlService.addUserMenuLinks(userMenuLinks); + }) + ); + + const helpObservable = chrome.getHelpSupportUrl$(); + if (cloud.isCloudEnabled) { - defer(() => security.authc.getCurrentUser()) - .pipe( - // Check if user is a cloud user. - map((user) => user.elastic_cloud_user), - // If user is not defined due to an unexpected error, then fail *open*. - catchError(() => of(true)), - filter((isElasticCloudUser) => isElasticCloudUser === true), - map(() => { - if (cloud.deploymentUrl) { - chrome.setCustomNavLink({ - title: i18n.translate('xpack.cloudLinks.deploymentLinkLabel', { - defaultMessage: 'Manage this deployment', - }), - euiIconType: 'logoCloud', - href: cloud.deploymentUrl, - }); - } - const userMenuLinks = createUserMenuLinks(cloud); - security.navControlService.addUserMenuLinks(userMenuLinks); - }) - ) - .subscribe(); + combineLatest({ user: userObservable, helpSupportUrl: helpObservable }).subscribe( + ({ helpSupportUrl }) => { + const helpMenuLinks = createHelpMenuLinks({ + docLinks, + helpSupportUrl, + }); + + chrome.setHelpMenuLinks(helpMenuLinks); + } + ); } } diff --git a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/user_menu_links.ts b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/user_menu_links.ts index 9b0ab8faac5a5..b6996a2f8f5a2 100644 --- a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/user_menu_links.ts +++ b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/user_menu_links.ts @@ -10,13 +10,14 @@ import type { CloudStart } from '@kbn/cloud-plugin/public'; import type { UserMenuLink } from '@kbn/security-plugin/public'; export const createUserMenuLinks = (cloud: CloudStart): UserMenuLink[] => { - const { profileUrl, organizationUrl } = cloud; + const { profileUrl, billingUrl, organizationUrl } = cloud; + const userMenuLinks = [] as UserMenuLink[]; if (profileUrl) { userMenuLinks.push({ label: i18n.translate('xpack.cloudLinks.userMenuLinks.profileLinkText', { - defaultMessage: 'Edit profile', + defaultMessage: 'Profile', }), iconType: 'user', href: profileUrl, @@ -25,14 +26,25 @@ export const createUserMenuLinks = (cloud: CloudStart): UserMenuLink[] => { }); } + if (billingUrl) { + userMenuLinks.push({ + label: i18n.translate('xpack.cloudLinks.userMenuLinks.billingLinkText', { + defaultMessage: 'Billing', + }), + iconType: 'visGauge', + href: billingUrl, + order: 200, + }); + } + if (organizationUrl) { userMenuLinks.push({ - label: i18n.translate('xpack.cloudLinks.userMenuLinks.accountLinkText', { - defaultMessage: 'Account & Billing', + label: i18n.translate('xpack.cloudLinks.userMenuLinks.organizationLinkText', { + defaultMessage: 'Organization', }), iconType: 'gear', href: organizationUrl, - order: 200, + order: 300, }); } diff --git a/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.tsx b/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.tsx index a2f8af345ac0a..1eb490ba0cd3a 100755 --- a/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.tsx +++ b/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.tsx @@ -43,7 +43,7 @@ export class CloudLinksPlugin }); } if (security) { - maybeAddCloudLinks({ security, chrome: core.chrome, cloud }); + maybeAddCloudLinks({ security, chrome: core.chrome, cloud, docLinks: core.docLinks }); } } } diff --git a/x-pack/plugins/cloud_integrations/cloud_links/tsconfig.json b/x-pack/plugins/cloud_integrations/cloud_links/tsconfig.json index ab4f52a347591..a57da9edc3199 100644 --- a/x-pack/plugins/cloud_integrations/cloud_links/tsconfig.json +++ b/x-pack/plugins/cloud_integrations/cloud_links/tsconfig.json @@ -17,6 +17,8 @@ "@kbn/i18n", "@kbn/i18n-react", "@kbn/guided-onboarding-plugin", + "@kbn/core-chrome-browser", + "@kbn/core-doc-links-browser", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/cloud_security_posture/common/constants.ts b/x-pack/plugins/cloud_security_posture/common/constants.ts index 97c718bf4aeb7..cc01567f55347 100644 --- a/x-pack/plugins/cloud_security_posture/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/common/constants.ts @@ -8,11 +8,18 @@ import { PostureTypes, VulnSeverity } from './types'; export const STATUS_ROUTE_PATH = '/internal/cloud_security_posture/status'; +export const STATUS_API_CURRENT_VERSION = '1'; + export const STATS_ROUTE_PATH = '/internal/cloud_security_posture/stats/{policy_template}'; + export const VULNERABILITIES_DASHBOARD_ROUTE_PATH = '/internal/cloud_security_posture/vulnerabilities_dashboard'; + export const BENCHMARKS_ROUTE_PATH = '/internal/cloud_security_posture/benchmarks'; +export const BENCHMARKS_API_CURRENT_VERSION = '1'; + export const FIND_CSP_RULE_TEMPLATE_ROUTE_PATH = '/internal/cloud_security_posture/rules/_find'; +export const FIND_CSP_RULE_TEMPLATE_API_CURRENT_VERSION = '1'; export const CLOUD_SECURITY_POSTURE_PACKAGE_NAME = 'cloud_security_posture'; // TODO: REMOVE CSP_LATEST_FINDINGS_DATA_VIEW and replace it with LATEST_FINDINGS_INDEX_PATTERN diff --git a/x-pack/plugins/cloud_security_posture/common/schemas/benchmark.ts b/x-pack/plugins/cloud_security_posture/common/schemas/benchmark.ts index 40f94ec63c57c..0d30ae992dd11 100644 --- a/x-pack/plugins/cloud_security_posture/common/schemas/benchmark.ts +++ b/x-pack/plugins/cloud_security_posture/common/schemas/benchmark.ts @@ -56,7 +56,7 @@ export const benchmarksQueryParamsSchema = schema.object({ /** * Benchmark filter */ - benchmark_name: schema.maybe(schema.string()), + package_policy_name: schema.maybe(schema.string()), }); export type BenchmarksQueryParams = TypeOf; diff --git a/x-pack/plugins/cloud_security_posture/common/types.ts b/x-pack/plugins/cloud_security_posture/common/types.ts index 4f3fd3434617c..f83fc5ae0fd73 100644 --- a/x-pack/plugins/cloud_security_posture/common/types.ts +++ b/x-pack/plugins/cloud_security_posture/common/types.ts @@ -111,7 +111,7 @@ export type PostureInput = typeof SUPPORTED_CLOUDBEAT_INPUTS[number]; export type CloudSecurityPolicyTemplate = typeof SUPPORTED_POLICY_TEMPLATES[number]; export type PosturePolicyTemplate = Extract; -export interface BenchmarkResponse { +export interface GetBenchmarkResponse { items: Benchmark[]; total: number; page: number; 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 99e277b30bb54..afb7a89c69e6f 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 @@ -8,7 +8,7 @@ import { useQuery, type UseQueryOptions } from '@tanstack/react-query'; import { useKibana } from '../hooks/use_kibana'; import { type CspSetupStatus } from '../../../common/types'; -import { STATUS_ROUTE_PATH } from '../../../common/constants'; +import { STATUS_API_CURRENT_VERSION, STATUS_ROUTE_PATH } from '../../../common/constants'; const getCspSetupStatusQueryKey = 'csp_status_key'; @@ -18,7 +18,7 @@ export const useCspSetupStatusApi = ( const { http } = useKibana().services; return useQuery( [getCspSetupStatusQueryKey], - () => http.get(STATUS_ROUTE_PATH), + () => http.get(STATUS_ROUTE_PATH, { version: STATUS_API_CURRENT_VERSION }), options ); }; diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/get_aws_credentials_form_options.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/get_aws_credentials_form_options.tsx index 4b245b9a992ac..b7c1e1d36b5cc 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/get_aws_credentials_form_options.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/get_aws_credentials_form_options.tsx @@ -103,10 +103,12 @@ export const getAwsCredentialsFormManualOptions = (): Array<{ value: AwsCredentialsType; text: string; }> => { - return Object.entries(getAwsCredentialsFormOptions()).map(([key, value]) => ({ - value: key as AwsCredentialsType, - text: value.label, - })); + return Object.entries(getAwsCredentialsFormOptions()) + .map(([key, value]) => ({ + value: key as AwsCredentialsType, + text: value.label, + })) + .filter(({ value }) => value !== 'cloud_formation'); }; export const DEFAULT_MANUAL_AWS_CREDENTIALS_TYPE = 'assume_role'; 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 ccdc3650b2596..95d955c69047e 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 @@ -7,7 +7,7 @@ import { useQuery } from '@tanstack/react-query'; import type { ListResult } from '@kbn/fleet-plugin/common'; -import { BENCHMARKS_ROUTE_PATH } from '../../../common/constants'; +import { BENCHMARKS_API_CURRENT_VERSION, BENCHMARKS_ROUTE_PATH } from '../../../common/constants'; import type { BenchmarksQueryParams } from '../../../common/schemas/benchmark'; import { useKibana } from '../../common/hooks/use_kibana'; import type { Benchmark } from '../../../common/types'; @@ -31,7 +31,7 @@ export const useCspBenchmarkIntegrations = ({ }: UseCspBenchmarkIntegrationsProps) => { const { http } = useKibana().services; const query: BenchmarksQueryParams = { - benchmark_name: name, + package_policy_name: name, per_page: perPage, page, sort_field: sortField, @@ -40,7 +40,11 @@ export const useCspBenchmarkIntegrations = ({ return useQuery( [QUERY_KEY, query], - () => http.get>(BENCHMARKS_ROUTE_PATH, { query }), + () => + http.get>(BENCHMARKS_ROUTE_PATH, { + query, + version: BENCHMARKS_API_CURRENT_VERSION, + }), { keepPreviousData: true } ); }; 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 b246fa3aa9188..a5aee47c7c1cc 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 @@ -10,6 +10,7 @@ import { useKibana } from '../../common/hooks/use_kibana'; import { CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE, + FIND_CSP_RULE_TEMPLATE_API_CURRENT_VERSION, FIND_CSP_RULE_TEMPLATE_ROUTE_PATH, } from '../../../common/constants'; @@ -27,7 +28,7 @@ export const useFindCspRuleTemplates = ( () => { return http.get(FIND_CSP_RULE_TEMPLATE_ROUTE_PATH, { query: { packagePolicyId, page, perPage }, - version: '1', + version: FIND_CSP_RULE_TEMPLATE_API_CURRENT_VERSION, }); } ); 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 c5ccc19d68663..290e5ecb31969 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 @@ -105,8 +105,8 @@ export const getCspPackagePolicies = async ( ? input.enabled : input.enabled && input.policy_template === postureType ).length > 0 && - (!queryParams.benchmark_name || - pkg.name.toLowerCase().includes(queryParams.benchmark_name.toLowerCase())) + (!queryParams.package_policy_name || + pkg.name.toLowerCase().includes(queryParams.package_policy_name.toLowerCase())) ); const page = queryParams?.page ?? 1; 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 8e46a5050553d..25fc95e1f8a20 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 @@ -27,7 +27,7 @@ describe('benchmarks API', () => { defineGetBenchmarksRoute(router); - const [config] = router.get.mock.calls[0]; + const [config] = router.versioned.get.mock.calls[0]; expect(config.path).toEqual('/internal/cloud_security_posture/benchmarks'); }); @@ -37,7 +37,9 @@ describe('benchmarks API', () => { defineGetBenchmarksRoute(router); - const [_, handler] = router.get.mock.calls[0]; + const versionedRouter = router.versioned.get.mock.results[0].value; + + const handler = versionedRouter.addVersion.mock.calls[0][1]; const mockContext = createCspRequestHandlerContextMock(); const mockResponse = httpServerMock.createResponseFactory(); @@ -54,7 +56,9 @@ describe('benchmarks API', () => { defineGetBenchmarksRoute(router); - const [_, handler] = router.get.mock.calls[0]; + const versionedRouter = router.versioned.get.mock.results[0].value; + + const handler = versionedRouter.addVersion.mock.calls[0][1]; const mockContext = createCspRequestHandlerContextMock(); mockContext.fleet.authz.fleet.all = false; @@ -78,15 +82,15 @@ describe('benchmarks API', () => { }); }); - it('expect to find benchmark_name', async () => { + it('expect to find package_policy_name', async () => { const validatedQuery = benchmarksQueryParamsSchema.validate({ - benchmark_name: 'my_cis_benchmark', + package_policy_name: 'my_cis_benchmark', }); expect(validatedQuery).toMatchObject({ page: 1, per_page: DEFAULT_BENCHMARKS_PER_PAGE, - benchmark_name: 'my_cis_benchmark', + package_policy_name: 'my_cis_benchmark', }); }); 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 6012583104f35..399e637b23494 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 @@ -15,7 +15,7 @@ import { POSTURE_TYPE_ALL, } from '../../../common/constants'; import { benchmarksQueryParamsSchema } from '../../../common/schemas/benchmark'; -import type { Benchmark } from '../../../common/types'; +import type { Benchmark, GetBenchmarkResponse } from '../../../common/types'; import { getBenchmarkFromPackagePolicy, getBenchmarkTypeFilter, @@ -84,63 +84,74 @@ const createBenchmarks = ( ); }; -export const defineGetBenchmarksRoute = (router: CspRouter): void => - router.get( - { +export const defineGetBenchmarksRoute = (router: CspRouter) => + router.versioned + .get({ + access: 'internal', path: BENCHMARKS_ROUTE_PATH, - validate: { query: benchmarksQueryParamsSchema }, options: { tags: ['access:cloud-security-posture-read'], }, - }, - async (context, request, response) => { - if (!(await context.fleet).authz.fleet.all) { - return response.forbidden(); - } + }) + .addVersion( + { + version: '1', + validate: { + request: { + query: benchmarksQueryParamsSchema, + }, + }, + }, + async (context, request, response) => { + if (!(await context.fleet).authz.fleet.all) { + return response.forbidden(); + } - const cspContext = await context.csp; + const cspContext = await context.csp; - try { - const cspPackagePolicies = await getCspPackagePolicies( - cspContext.soClient, - cspContext.packagePolicyService, - CLOUD_SECURITY_POSTURE_PACKAGE_NAME, - request.query, - POSTURE_TYPE_ALL - ); + try { + const cspPackagePolicies = await getCspPackagePolicies( + cspContext.soClient, + cspContext.packagePolicyService, + CLOUD_SECURITY_POSTURE_PACKAGE_NAME, + request.query, + POSTURE_TYPE_ALL + ); - const agentPolicies = await getCspAgentPolicies( - cspContext.soClient, - cspPackagePolicies.items, - cspContext.agentPolicyService - ); + const agentPolicies = await getCspAgentPolicies( + cspContext.soClient, + cspPackagePolicies.items, + cspContext.agentPolicyService + ); - const agentStatusesByAgentPolicyId = await getAgentStatusesByAgentPolicies( - cspContext.agentService, - agentPolicies, - cspContext.logger - ); + const agentStatusesByAgentPolicyId = await getAgentStatusesByAgentPolicies( + cspContext.agentService, + agentPolicies, + cspContext.logger + ); - const benchmarks = await createBenchmarks( - cspContext.soClient, - agentPolicies, - agentStatusesByAgentPolicyId, - cspPackagePolicies.items - ); + const benchmarks = await createBenchmarks( + cspContext.soClient, + agentPolicies, + agentStatusesByAgentPolicyId, + cspPackagePolicies.items + ); - return response.ok({ - body: { + const getBenchmarkResponse: GetBenchmarkResponse = { ...cspPackagePolicies, items: benchmarks, - }, - }); - } catch (err) { - const error = transformError(err); - cspContext.logger.error(`Failed to fetch benchmarks ${err}`); - return response.customError({ - body: { message: error.message }, - statusCode: error.statusCode, - }); + }; + + return response.ok({ + body: getBenchmarkResponse, + }); + } catch (err) { + const error = transformError(err); + cspContext.logger.error(`Failed to fetch benchmarks ${err}`); + return response.customError({ + body: { message: error.message }, + statusCode: error.statusCode, + }); + } } - } - ); + ); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/csp_rule_template/get_csp_rule_template.ts b/x-pack/plugins/cloud_security_posture/server/routes/csp_rule_template/get_csp_rule_template.ts index 6d41f87eaff7f..63e2bdb6ee102 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/csp_rule_template/get_csp_rule_template.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/csp_rule_template/get_csp_rule_template.ts @@ -7,7 +7,6 @@ import { NewPackagePolicy } from '@kbn/fleet-plugin/common'; import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; -import pMap from 'p-map'; import { transformError } from '@kbn/securitysolution-es-utils'; import { GetCspRuleTemplateRequest, GetCspRuleTemplateResponse } from '../../../common/types'; import { CspRuleTemplate } from '../../../common/schemas'; @@ -61,13 +60,9 @@ const findCspRuleTemplateHandler = async ( filter: getBenchmarkTypeFilter(benchmarkId), }); - const cspRulesTemplates = await pMap( - cspRulesTemplatesSo.saved_objects, - async (cspRuleTemplate) => { - return { ...cspRuleTemplate.attributes }; - }, - { concurrency: 50 } - ); + const cspRulesTemplates = cspRulesTemplatesSo.saved_objects.map((cspRuleTemplate) => { + return { ...cspRuleTemplate.attributes }; + }); return { items: cspRulesTemplates, 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 60f634b4c32b8..86b0d0a66802b 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 @@ -16,6 +16,7 @@ import type { import moment from 'moment'; import { Installation, PackagePolicy } from '@kbn/fleet-plugin/common'; import { schema } from '@kbn/config-schema'; +import { VersionedRoute } from '@kbn/core-http-server/src/versioning/types'; import { CLOUD_SECURITY_POSTURE_PACKAGE_NAME, STATUS_ROUTE_PATH, @@ -29,7 +30,12 @@ import { LATEST_VULNERABILITIES_INDEX_DEFAULT_NS, VULN_MGMT_POLICY_TEMPLATE, } from '../../../common/constants'; -import type { CspApiRequestHandlerContext, CspRouter, StatusResponseInfo } from '../../types'; +import type { + CspApiRequestHandlerContext, + CspRequestHandlerContext, + CspRouter, + StatusResponseInfo, +} from '../../types'; import type { CspSetupStatus, CspStatusCode, @@ -328,44 +334,55 @@ export const statusQueryParamsSchema = schema.object({ check: schema.oneOf([schema.literal('all'), schema.literal('init')], { defaultValue: 'all' }), }); -export const defineGetCspStatusRoute = (router: CspRouter): void => - router.get( - { +export const defineGetCspStatusRoute = ( + router: CspRouter +): VersionedRoute<'get', CspRequestHandlerContext> => + router.versioned + .get({ + access: 'internal', path: STATUS_ROUTE_PATH, - validate: { query: statusQueryParamsSchema }, options: { tags: ['access:cloud-security-posture-read'], }, - }, - async (context, request, response) => { - const cspContext = await context.csp; - try { - if (request.query.check === 'init') { + }) + .addVersion( + { + version: '1', + validate: { + request: { + query: statusQueryParamsSchema, + }, + }, + }, + async (context, request, response) => { + const cspContext = await context.csp; + try { + if (request.query.check === 'init') { + return response.ok({ + body: { + isPluginInitialized: cspContext.isPluginInitialized(), + }, + }); + } + const status: CspSetupStatus = await getCspStatus({ + ...cspContext, + esClient: cspContext.esClient.asCurrentUser, + }); return response.ok({ - body: { - isPluginInitialized: cspContext.isPluginInitialized(), - }, + body: status, + }); + } catch (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, }); } - const status = await getCspStatus({ - ...cspContext, - esClient: cspContext.esClient.asCurrentUser, - }); - return response.ok({ - body: status, - }); - } catch (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, - }); } - } - ); + ); const getStatusResponse = (statusResponseInfo: StatusResponseInfo) => { const { diff --git a/x-pack/plugins/cloud_security_posture/tsconfig.json b/x-pack/plugins/cloud_security_posture/tsconfig.json index 6dd9ba33a3165..ae8f7d610002b 100755 --- a/x-pack/plugins/cloud_security_posture/tsconfig.json +++ b/x-pack/plugins/cloud_security_posture/tsconfig.json @@ -48,6 +48,7 @@ "@kbn/shared-ux-router", "@kbn/core-saved-objects-server", "@kbn/share-plugin", + "@kbn/core-http-server", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/enterprise_search/common/types/connectors.ts b/x-pack/plugins/enterprise_search/common/types/connectors.ts index a1e1b4be9a7a4..1ea3096f1b2e7 100644 --- a/x-pack/plugins/enterprise_search/common/types/connectors.ts +++ b/x-pack/plugins/enterprise_search/common/types/connectors.ts @@ -70,13 +70,6 @@ export type ConnectorConfiguration = Record< use_text_extraction_service?: ConnectorConfigProperties; // This only exists for SharePoint Online }; -export interface ConnectorSyncConfigProperties { - label: string; - value: string | number | boolean | null; -} - -export type ConnectorSyncConfiguration = Record; - export interface ConnectorScheduling { enabled: boolean; interval: string; // interval has crontab syntax @@ -249,7 +242,7 @@ export interface ConnectorSyncJob { canceled_at: string | null; completed_at: string | null; connector: { - configuration: ConnectorSyncConfiguration; + configuration: ConnectorConfiguration; filtering: FilteringRules | FilteringRules[] | null; id: string; index_name: string; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_scheduling/connector_cron_editor.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_scheduling/connector_cron_editor.tsx index 6ddc393c45fad..46eccba6a4f70 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_scheduling/connector_cron_editor.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_scheduling/connector_cron_editor.tsx @@ -22,6 +22,7 @@ import { ConnectorSchedulingLogic } from '../connector_scheduling_logic'; interface ConnectorCronEditorProps { disabled?: boolean; + frequencyBlockList?: string[]; onReset?(): void; onSave?(interval: ConnectorScheduling['interval']): void; scheduling: ConnectorScheduling; @@ -30,6 +31,7 @@ interface ConnectorCronEditorProps { export const ConnectorCronEditor: React.FC = ({ disabled = false, + frequencyBlockList = ['MINUTE'], scheduling, onSave, onReset, @@ -77,7 +79,7 @@ export const ConnectorCronEditor: React.FC = ({ setNewInterval(expression); setHasChanges(type); }} - frequencyBlockList={['MINUTE']} + frequencyBlockList={frequencyBlockList} /> @@ -133,7 +135,7 @@ function cronToFrequency(cron: string): Frequency { if (fields.length < 4) { return 'YEAR'; } - if (fields[1] === '*') { + if (fields[1] === '*' || fields[1].startsWith('*/')) { return 'MINUTE'; } if (fields[2] === '*') { @@ -142,7 +144,7 @@ function cronToFrequency(cron: string): Frequency { if (fields[3] === '*') { return 'DAY'; } - if (fields[4] === '?') { + if (fields[3] === '?') { return 'WEEK'; } if (fields[4] === '*') { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_scheduling/full_content.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_scheduling/full_content.tsx index da8aaa3bb1a75..3fbe4ef8859f2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_scheduling/full_content.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_scheduling/full_content.tsx @@ -215,6 +215,7 @@ export const ConnectorContentScheduling: React.FC { diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/__snapshots__/cron_editor.test.tsx.snap b/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/__snapshots__/cron_editor.test.tsx.snap index e00fb47fc377d..2e22e3bfe30b0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/__snapshots__/cron_editor.test.tsx.snap +++ b/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/__snapshots__/cron_editor.test.tsx.snap @@ -3387,6 +3387,995 @@ exports[`CronEditor is rendered with a MINUTE frequency 1`] = ` + + + } + labelType="label" + > +
+
+ + + +
+
+ + +
+ + + +
+ + + + +
+ + + + + +
+
+
+ + + +
+
+
+
+
+
+
`; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/constants.ts b/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/constants.ts index ad0c2bb45cc39..e2ef66324309f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/constants.ts @@ -24,6 +24,11 @@ function makeSequence(min: number, max: number): number[] { return values; } +export const EVERY_MINUTE_OPTIONS = makeSequence(1, 59).map((value) => ({ + value: '*/' + value.toString(), + text: value.toString(), +})); + export const MINUTE_OPTIONS = makeSequence(0, 59).map((value) => ({ value: value.toString(), text: padStart(value.toString(), 2, '0'), @@ -77,7 +82,7 @@ export const UNITS: EuiSelectOption[] = [ ]; export const frequencyToFieldsMap: Record = { - MINUTE: {}, + MINUTE: { minute: true }, HOUR: { minute: true, }, @@ -106,7 +111,7 @@ export const frequencyToFieldsMap: Record = { export const frequencyToBaselineFieldsMap: Record = { MINUTE: { second: '0', - minute: '*', + minute: '*/1', hour: '*', date: '*', month: '*', diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/cron_editor.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/cron_editor.test.tsx index 44f69c9fa6e33..668105a93253e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/cron_editor.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/cron_editor.test.tsx @@ -72,6 +72,20 @@ describe('CronEditor', () => { const minuteSelect = findTestSubject(component, 'cronFrequencyYearlyMinuteSelect'); expect(minuteSelect.props().value).toBe('20'); }); + + it('sets the values of the fields for minute', () => { + const component = mountWithI18nProvider( + {}} + /> + ); + + const monthSelect = findTestSubject(component, 'cronFrequencyMinutelyMinuteSelect'); + expect(monthSelect.prop('value')).toBe('*/18'); + }); }); describe('onChange', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/cron_editor.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/cron_editor.tsx index 06e37aa2366f8..bcda5c37f33c7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/cron_editor.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/cron_editor.tsx @@ -20,13 +20,16 @@ import { UNITS, frequencyToFieldsMap, frequencyToBaselineFieldsMap, + EVERY_MINUTE_OPTIONS, } from './constants'; import { CronDaily } from './cron_daily'; import { CronHourly } from './cron_hourly'; +import { CronMinutely } from './cron_minutely'; import { CronMonthly } from './cron_monthly'; import { CronWeekly } from './cron_weekly'; import { CronYearly } from './cron_yearly'; import { cronExpressionToParts, cronPartsToExpression } from './services'; +import { convertFromEveryXMinute, convertToEveryXMinute } from './services/cron'; import { Frequency, Field, FieldToValueMap } from './types'; const excludeBlockListedFrequencies = ( @@ -41,10 +44,12 @@ const excludeBlockListedFrequencies = ( }; interface Props { - frequencyBlockList?: string[]; + autoFocus?: boolean; + cronExpression: string; + disabled?: boolean; fieldToPreferredValueMap: FieldToValueMap; frequency: Frequency; - cronExpression: string; + frequencyBlockList?: string[]; onChange: ({ cronExpression, fieldToPreferredValueMap, @@ -54,8 +59,6 @@ interface Props { fieldToPreferredValueMap: FieldToValueMap; frequency: Frequency; }) => void; - autoFocus?: boolean; - disabled?: boolean; } type State = FieldToValueMap; @@ -77,14 +80,20 @@ export class CronEditor extends Component { } onChangeFrequency = (frequency: Frequency) => { - const { onChange, fieldToPreferredValueMap } = this.props; + const { onChange, fieldToPreferredValueMap, frequency: oldFrequency } = this.props; // Update fields which aren't editable with acceptable baseline values. const editableFields = Object.keys(frequencyToFieldsMap[frequency]) as Field[]; const inheritedFields = editableFields.reduce( (fieldBaselines, field) => { if (fieldToPreferredValueMap[field] != null) { - fieldBaselines[field] = fieldToPreferredValueMap[field]; + if (oldFrequency === 'MINUTE') { + fieldBaselines[field] = convertFromEveryXMinute(fieldToPreferredValueMap[field]); + } else if (frequency === 'MINUTE') { + fieldBaselines[field] = convertToEveryXMinute(fieldToPreferredValueMap[field]); + } else { + fieldBaselines[field] = fieldToPreferredValueMap[field]; + } } return fieldBaselines; }, @@ -143,7 +152,14 @@ export class CronEditor extends Component { switch (frequency) { case 'MINUTE': - return; + return ( + + ); case 'HOUR': return ( diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/cron_minutely.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/cron_minutely.tsx new file mode 100644 index 0000000000000..1aedb9737e5ce --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/cron_minutely.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { Fragment } from 'react'; + +import { EuiFormRow, EuiSelect, EuiSelectOption } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; + +interface Props { + disabled?: boolean; + minute?: string; + minuteOptions: EuiSelectOption[]; + onChange: ({ minute }: { minute?: string }) => void; +} + +export const CronMinutely: React.FunctionComponent = ({ + disabled, + minute, + minuteOptions, + onChange, +}) => ( + + + } + fullWidth + data-test-subj="cronFrequencyConfiguration" + > + onChange({ minute: e.target.value })} + fullWidth + prepend={i18n.translate( + 'xpack.enterpriseSearch.cronEditor.cronMinutely.fieldMinute.textAtLabel', + { + defaultMessage: 'Every', + } + )} + append={i18n.translate( + 'xpack.enterpriseSearch.cronEditor.cronMinutely.fieldMinute.textAppendLabel', + { + defaultMessage: 'minutes', + } + )} + data-test-subj="cronFrequencyMinutelyMinuteSelect" + /> + + +); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/services/cron.ts b/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/services/cron.ts index 542502fbcbe76..fc2019d63c17d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/services/cron.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/cron_editor/services/cron.ts @@ -56,3 +56,18 @@ export function cronPartsToExpression({ }: FieldToValueMap): string { return `${second} ${minute} ${hour} ${date} ${month} ${day}`; } + +export function convertToEveryXMinute( + minute: FieldToValueMap['minute'] +): FieldToValueMap['minute'] { + if (!minute) return minute; + if (minute.startsWith('*/')) return minute; + return '*/' + minute; +} + +export function convertFromEveryXMinute( + minute: FieldToValueMap['minute'] +): FieldToValueMap['minute'] { + if (!minute) return minute; + return minute.startsWith('*/') ? minute.slice(2) : minute; +} diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts index 8d2ac4715e8df..e4ef3288819f6 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/start_sync.ts @@ -17,9 +17,8 @@ import { isConfigEntry } from '../../../common/connectors/is_category_entry'; import { ENTERPRISE_SEARCH_CONNECTOR_CRAWLER_SERVICE_TYPE } from '../../../common/constants'; import { + ConnectorConfiguration, ConnectorDocument, - ConnectorSyncConfiguration, - ConnectorSyncJobDocument, SyncJobType, SyncStatus, TriggerMethod, @@ -38,13 +37,13 @@ export const startConnectorSync = async ( }); const connector = connectorResult._source; if (connector) { - const config = Object.entries(connector.configuration).reduce((prev, [key, configEntry]) => { + const config = Object.entries(connector.configuration).reduce((acc, [key, configEntry]) => { if (isConfigEntry(configEntry)) { - prev[key] = { label: configEntry.label, value: configEntry.value }; + acc[key] = configEntry; } - return prev; - }, {} as ConnectorSyncConfiguration); - const configuration: ConnectorSyncConfiguration = nextSyncConfig + return acc; + }, {} as ConnectorConfiguration); + const configuration = nextSyncConfig ? { ...config, nextSyncConfig: { label: 'nextSyncConfig', value: nextSyncConfig }, @@ -73,7 +72,7 @@ export const startConnectorSync = async ( ? `${CONNECTORS_ACCESS_CONTROL_INDEX_PREFIX}${indexNameWithoutSearchPrefix}` : index_name; - return await client.asCurrentUser.index({ + return await client.asCurrentUser.index({ document: { cancelation_requested_at: null, canceled_at: null, diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.test.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.test.ts index e80d9ed37d916..d3193b1855d6b 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.test.ts @@ -8,9 +8,8 @@ import { MockRouter, mockDependencies } from '../../__mocks__'; import { RequestHandlerContext } from '@kbn/core/server'; -import { MlTrainedModels } from '@kbn/ml-plugin/server'; -import { SharedServices } from '@kbn/ml-plugin/server/shared_services'; +import type { MlPluginSetup, MlTrainedModels } from '@kbn/ml-plugin/server'; import { ErrorCode } from '../../../common/types/error_codes'; @@ -168,7 +167,7 @@ describe('Enterprise Search Managed Indices', () => { }); describe('GET /internal/enterprise_search/indices/{indexName}/ml_inference/pipeline_processors', () => { - let mockMl: SharedServices; + let mockMl: MlPluginSetup; let mockTrainedModelsProvider: MlTrainedModels; beforeEach(() => { @@ -195,7 +194,7 @@ describe('Enterprise Search Managed Indices', () => { mockMl = { trainedModelsProvider: () => Promise.resolve(mockTrainedModelsProvider), - } as unknown as jest.Mocked; + } as unknown as jest.Mocked; registerIndexRoutes({ ...mockDependencies, @@ -1069,7 +1068,7 @@ describe('Enterprise Search Managed Indices', () => { describe('GET /internal/enterprise_search/pipelines/ml_inference', () => { let mockTrainedModelsProvider: MlTrainedModels; - let mockMl: SharedServices; + let mockMl: MlPluginSetup; beforeEach(() => { const context = { @@ -1095,7 +1094,7 @@ describe('Enterprise Search Managed Indices', () => { mockMl = { trainedModelsProvider: () => Promise.resolve(mockTrainedModelsProvider), - } as unknown as jest.Mocked; + } as unknown as jest.Mocked; registerIndexRoutes({ ...mockDependencies, @@ -1134,7 +1133,7 @@ describe('Enterprise Search Managed Indices', () => { }); describe('POST /internal/enterprise_search/ml/models/{modelName}', () => { - let mockMl: SharedServices; + let mockMl: MlPluginSetup; let mockTrainedModelsProvider: MlTrainedModels; beforeEach(() => { @@ -1156,7 +1155,7 @@ describe('Enterprise Search Managed Indices', () => { mockMl = { trainedModelsProvider: () => Promise.resolve(mockTrainedModelsProvider), - } as unknown as jest.Mocked; + } as unknown as jest.Mocked; registerIndexRoutes({ ...mockDependencies, @@ -1198,7 +1197,7 @@ describe('Enterprise Search Managed Indices', () => { }); describe('POST /internal/enterprise_search/ml/models/{modelName}/deploy', () => { - let mockMl: SharedServices; + let mockMl: MlPluginSetup; let mockTrainedModelsProvider: MlTrainedModels; beforeEach(() => { @@ -1220,7 +1219,7 @@ describe('Enterprise Search Managed Indices', () => { mockMl = { trainedModelsProvider: () => Promise.resolve(mockTrainedModelsProvider), - } as unknown as jest.Mocked; + } as unknown as jest.Mocked; registerIndexRoutes({ ...mockDependencies, @@ -1262,7 +1261,7 @@ describe('Enterprise Search Managed Indices', () => { }); describe('GET /internal/enterprise_search/ml/models/{modelName}', () => { - let mockMl: SharedServices; + let mockMl: MlPluginSetup; let mockTrainedModelsProvider: MlTrainedModels; beforeEach(() => { @@ -1283,7 +1282,7 @@ describe('Enterprise Search Managed Indices', () => { mockMl = { trainedModelsProvider: () => Promise.resolve(mockTrainedModelsProvider), - } as unknown as jest.Mocked; + } as unknown as jest.Mocked; registerIndexRoutes({ ...mockDependencies, diff --git a/x-pack/plugins/fleet/common/constants/file_storage.ts b/x-pack/plugins/fleet/common/constants/file_storage.ts index 554176f40e263..0d796d691b97b 100644 --- a/x-pack/plugins/fleet/common/constants/file_storage.ts +++ b/x-pack/plugins/fleet/common/constants/file_storage.ts @@ -8,26 +8,26 @@ // File storage indexes supporting file upload from the host to Elastic/Kibana // If needing to get an integration specific index name, use the utility functions // found in `common/services/file_storage` -export const FILE_STORAGE_METADATA_INDEX_PATTERN = '.fleet-files-*'; -export const FILE_STORAGE_DATA_INDEX_PATTERN = '.fleet-file-data-*'; +export const FILE_STORAGE_METADATA_INDEX_PATTERN = '.fleet-fileds-fromhost-meta-*'; +export const FILE_STORAGE_DATA_INDEX_PATTERN = '.fleet-fileds-fromhost-data-*'; -// File storage indexes supporting user uplaoded files (via kibana) that will be +// File storage indexes supporting user uploaded files (via kibana) that will be // delivered to the host agent/endpoint -export const FILE_STORAGE_TO_HOST_METADATA_INDEX_PATTERN = '.fleet-filedelivery-meta-*'; -export const FILE_STORAGE_TO_HOST_DATA_INDEX_PATTERN = '.fleet-filedelivery-data-*'; +export const FILE_STORAGE_TO_HOST_METADATA_INDEX_PATTERN = '.fleet-fileds-tohost-meta-*'; +export const FILE_STORAGE_TO_HOST_DATA_INDEX_PATTERN = '.fleet-fileds-tohost-data-*'; // which integrations support file upload and the name to use for the file upload index export const FILE_STORAGE_INTEGRATION_INDEX_NAMES: Readonly< Record< string, - { + Readonly<{ /** name to be used for the index */ name: string; /** If integration supports files sent from host to ES/Kibana */ fromHost: boolean; /** If integration supports files to be sent to host from kibana */ toHost: boolean; - } + }> > > = { elastic_agent: { name: 'agent', fromHost: true, toHost: false }, diff --git a/x-pack/plugins/fleet/common/services/file_storage.test.ts b/x-pack/plugins/fleet/common/services/file_storage.test.ts index 0360d7311eb6a..dbf5da61dba1d 100644 --- a/x-pack/plugins/fleet/common/services/file_storage.test.ts +++ b/x-pack/plugins/fleet/common/services/file_storage.test.ts @@ -5,24 +5,41 @@ * 2.0. */ +import { FILE_STORAGE_METADATA_INDEX_PATTERN } from '../constants'; + import { getFileDataIndexName, getFileMetadataIndexName } from '..'; +import { getIntegrationNameFromIndexName } from './file_storage'; + describe('File Storage services', () => { describe('File Index Names', () => { it('should generate file metadata index name for files received from host', () => { - expect(getFileMetadataIndexName('foo')).toEqual('.fleet-files-foo'); + expect(getFileMetadataIndexName('foo')).toEqual('.fleet-fileds-fromhost-meta-foo'); }); it('should generate file data index name for files received from host', () => { - expect(getFileDataIndexName('foo')).toEqual('.fleet-file-data-foo'); + expect(getFileDataIndexName('foo')).toEqual('.fleet-fileds-fromhost-data-foo'); }); it('should generate file metadata index name for files to be delivered to host', () => { - expect(getFileMetadataIndexName('foo', true)).toEqual('.fleet-filedelivery-meta-foo'); + expect(getFileMetadataIndexName('foo', true)).toEqual('.fleet-fileds-tohost-meta-foo'); }); it('should generate file data index name for files to be delivered to host', () => { - expect(getFileDataIndexName('foo', true)).toEqual('.fleet-filedelivery-data-foo'); + expect(getFileDataIndexName('foo', true)).toEqual('.fleet-fileds-tohost-data-foo'); + }); + }); + + describe('getIntegrationNameFromIndexName()', () => { + it.each([ + ['regular index names', '.fleet-fileds-fromhost-meta-agent'], + ['datastream index names', '.ds-.fleet-fileds-fromhost-data-agent-2023.06.30-00001'], + ])('should handle %s', (_, index) => { + expect(getIntegrationNameFromIndexName(index, FILE_STORAGE_METADATA_INDEX_PATTERN)).toEqual( + 'agent' + ); }); + + it.todo('should error if index pattern does not include `*`'); }); }); diff --git a/x-pack/plugins/fleet/common/services/file_storage.ts b/x-pack/plugins/fleet/common/services/file_storage.ts index 6581f671df663..af909a22aa946 100644 --- a/x-pack/plugins/fleet/common/services/file_storage.ts +++ b/x-pack/plugins/fleet/common/services/file_storage.ts @@ -56,21 +56,19 @@ export const getFileDataIndexName = ( ); }; -/** - * Returns the write index name for a given file upload alias name, this is the same for metadata and chunks - * @param aliasName - */ -export const getFileWriteIndexName = (aliasName: string) => aliasName + '-000001'; /** * Returns back the integration name for a given File Data (chunks) index name. * * @example - * // Given a File data index pattern of `.fleet-file-data-*`: + * // Given a File data index pattern of `.fleet-fileds-fromhost-data-*`: * - * getIntegrationNameFromFileDataIndexName('.fleet-file-data-agent'); + * getIntegrationNameFromFileDataIndexName('.fleet-fileds-fromhost-data-agent'); * // return 'agent' * - * getIntegrationNameFromFileDataIndexName('.fleet-file-data-agent-00001'); + * getIntegrationNameFromFileDataIndexName('.ds-.fleet-fileds-fromhost-data-agent'); + * // return 'agent' + * + * getIntegrationNameFromFileDataIndexName('.ds-.fleet-fileds-fromhost-data-agent-2023.06.30-00001'); * // return 'agent' */ export const getIntegrationNameFromFileDataIndexName = (indexName: string): string => { @@ -87,7 +85,7 @@ export const getIntegrationNameFromIndexName = ( throw new Error(`Unable to parse index name. No '*' in index pattern: ${indexPattern}`); } - const indexPieces = indexName.split('-'); + const indexPieces = indexName.replace(/^\.ds-/, '').split('-'); if (indexPieces[integrationNameIndexPosition]) { return indexPieces[integrationNameIndexPosition]; @@ -95,15 +93,3 @@ export const getIntegrationNameFromIndexName = ( throw new Error(`Index name ${indexName} does not seem to be a File storage index`); }; - -export const getFileStorageWriteIndexBody = (aliasName: string) => ({ - aliases: { - [aliasName]: { - is_write_index: true, - }, - }, - settings: { - 'index.lifecycle.rollover_alias': aliasName, - 'index.hidden': true, - }, -}); diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.ts index f5ef34d28b5c4..1780d3e55169a 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/install.ts @@ -11,18 +11,9 @@ import type { ElasticsearchClient, Logger } from '@kbn/core/server'; import type { IndicesCreateRequest } from '@elastic/elasticsearch/lib/api/types'; -import { - FILE_STORAGE_INTEGRATION_INDEX_NAMES, - FILE_STORAGE_INTEGRATION_NAMES, -} from '../../../../../common/constants'; - import { ElasticsearchAssetType } from '../../../../types'; import { - getFileWriteIndexName, - getFileStorageWriteIndexBody, getPipelineNameForDatastream, - getFileDataIndexName, - getFileMetadataIndexName, getRegistryDataStreamAssetBaseName, } from '../../../../../common/services'; import type { @@ -440,63 +431,6 @@ export async function ensureDefaultComponentTemplates( ); } -/* - * Given a list of integration names, if the integrations support file upload - * then ensure that the alias has a matching write index, as we use "plain" indices - * not data streams. - * e.g .fleet-file-data-agent must have .fleet-file-data-agent-00001 as the write index - * before files can be uploaded. - */ -export async function ensureFileUploadWriteIndices(opts: { - esClient: ElasticsearchClient; - logger: Logger; - integrationNames: string[]; -}) { - const { esClient, logger, integrationNames } = opts; - - const integrationsWithFileUpload = integrationNames.filter((integration) => - FILE_STORAGE_INTEGRATION_NAMES.includes(integration as any) - ); - - if (!integrationsWithFileUpload.length) return []; - - const ensure = (aliasName: string) => - ensureAliasHasWriteIndex({ - esClient, - logger, - aliasName, - writeIndexName: getFileWriteIndexName(aliasName), - body: getFileStorageWriteIndexBody(aliasName), - }); - - return Promise.all( - integrationsWithFileUpload.flatMap((integrationName) => { - const { - name: indexName, - fromHost, - toHost, - } = FILE_STORAGE_INTEGRATION_INDEX_NAMES[integrationName]; - const indexCreateRequests: Array> = []; - - if (fromHost) { - indexCreateRequests.push( - ensure(getFileDataIndexName(indexName)), - ensure(getFileMetadataIndexName(indexName)) - ); - } - - if (toHost) { - indexCreateRequests.push( - ensure(getFileDataIndexName(indexName, true)), - ensure(getFileMetadataIndexName(indexName, true)) - ); - } - - return indexCreateRequests; - }) - ); -} - export async function ensureComponentTemplate( esClient: ElasticsearchClient, logger: Logger, diff --git a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts index b884e8c893de8..a5b4ad6f4e00b 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts @@ -37,7 +37,6 @@ import type { PackageVerificationResult, IndexTemplateEntry, } from '../../../types'; -import { ensureFileUploadWriteIndices } from '../elasticsearch/template/install'; import { removeLegacyTemplates } from '../elasticsearch/template/remove_legacy'; import { isTopLevelPipeline, deletePreviousPipelines } from '../elasticsearch/ingest_pipeline'; import { installILMPolicy } from '../elasticsearch/ilm/install'; @@ -236,15 +235,6 @@ export async function _installPackage({ logger.warn(`Error removing legacy templates: ${e.message}`); } - const { diagnosticFileUploadEnabled } = appContextService.getExperimentalFeatures(); - if (diagnosticFileUploadEnabled) { - await ensureFileUploadWriteIndices({ - integrationNames: [packageInfo.name], - esClient, - logger, - }); - } - // update current backing indices of each data stream await withPackageSpan('Update write indices', () => updateCurrentWriteIndices(esClient, logger, indexTemplates) diff --git a/x-pack/plugins/fleet/server/services/files/client_from_host.test.ts b/x-pack/plugins/fleet/server/services/files/client_from_host.test.ts index 068bad018b5e8..0051418b1c00d 100644 --- a/x-pack/plugins/fleet/server/services/files/client_from_host.test.ts +++ b/x-pack/plugins/fleet/server/services/files/client_from_host.test.ts @@ -91,11 +91,11 @@ describe('FleetFromHostFilesClient', () => { esClientMock.search.mockImplementation(async (searchRequest = {}) => { // File metadata - if ((searchRequest.index as string).startsWith('.fleet-files-')) { + if ((searchRequest.index as string).startsWith('.fleet-fileds-fromhost-meta-')) { return fleetFilesIndexSearchResponse; } - if ((searchRequest.index as string).startsWith('.fleet-file-data-')) { + if ((searchRequest.index as string).startsWith('.fleet-fileds-fromhost-data-')) { return fleetFileDataIndexSearchResponse; } @@ -111,8 +111,8 @@ describe('FleetFromHostFilesClient', () => { expect(createEsFileClientMock).toHaveBeenCalledWith({ elasticsearchClient: esClientMock, logger: loggerMock, - metadataIndex: '.fleet-files-foo', - blobStorageIndex: '.fleet-file-data-foo', + metadataIndex: '.fleet-fileds-fromhost-meta-foo', + blobStorageIndex: '.fleet-fileds-fromhost-data-foo', indexIsAlias: true, }); }); @@ -159,7 +159,7 @@ describe('FleetFromHostFilesClient', () => { }, }, }, - index: '.fleet-file-data-foo', + index: '.fleet-fileds-fromhost-data-foo', size: 0, }); }); diff --git a/x-pack/plugins/fleet/server/services/files/client_to_host.test.ts b/x-pack/plugins/fleet/server/services/files/client_to_host.test.ts index a4820f256da95..1068f34366970 100644 --- a/x-pack/plugins/fleet/server/services/files/client_to_host.test.ts +++ b/x-pack/plugins/fleet/server/services/files/client_to_host.test.ts @@ -130,8 +130,8 @@ describe('FleetToHostFilesClient', () => { expect(createEsFileClientMock).toHaveBeenCalledWith({ elasticsearchClient: esClientMock, logger: loggerMock, - metadataIndex: '.fleet-filedelivery-meta-foo', - blobStorageIndex: '.fleet-filedelivery-data-foo', + metadataIndex: '.fleet-fileds-tohost-meta-foo', + blobStorageIndex: '.fleet-fileds-tohost-data-foo', maxSizeBytes: 12345, indexIsAlias: true, }); diff --git a/x-pack/plugins/fleet/server/services/files/index.ts b/x-pack/plugins/fleet/server/services/files/index.ts index 5790164b81d07..8d6cbdb9fd5a4 100644 --- a/x-pack/plugins/fleet/server/services/files/index.ts +++ b/x-pack/plugins/fleet/server/services/files/index.ts @@ -34,22 +34,27 @@ export async function getFilesByStatus( abortController: AbortController, status: FileStatus = 'READY' ): Promise { - const result = await esClient.search( - { - index: FILE_STORAGE_METADATA_INDEX_PATTERN, - body: { - size: ES_SEARCH_LIMIT, - query: { - term: { - 'file.Status': status, + const result = await esClient + .search( + { + index: FILE_STORAGE_METADATA_INDEX_PATTERN, + body: { + size: ES_SEARCH_LIMIT, + query: { + term: { + 'file.Status': status, + }, }, + _source: false, }, - _source: false, + ignore_unavailable: true, }, - ignore_unavailable: true, - }, - { signal: abortController.signal } - ); + { signal: abortController.signal } + ) + .catch((err) => { + Error.captureStackTrace(err); + throw err; + }); return result.hits.hits; } @@ -84,32 +89,37 @@ export async function fileIdsWithoutChunksByIndex( return acc; }, {} as FileIdsByIndex); - const chunks = await esClient.search<{ bid: string }>( - { - index: FILE_STORAGE_DATA_INDEX_PATTERN, - body: { - size: ES_SEARCH_LIMIT, - query: { - bool: { - must: [ - { - terms: { - bid: Array.from(allFileIds), + const chunks = await esClient + .search<{ bid: string }>( + { + index: FILE_STORAGE_DATA_INDEX_PATTERN, + body: { + size: ES_SEARCH_LIMIT, + query: { + bool: { + must: [ + { + terms: { + bid: Array.from(allFileIds), + }, }, - }, - { - term: { - last: true, + { + term: { + last: true, + }, }, - }, - ], + ], + }, }, + _source: ['bid'], }, - _source: ['bid'], }, - }, - { signal: abortController.signal } - ); + { signal: abortController.signal } + ) + .catch((err) => { + Error.captureStackTrace(err); + throw err; + }); chunks.hits.hits.forEach((hit) => { const fileId = hit._source?.bid; @@ -140,22 +150,27 @@ export function updateFilesStatus( ): Promise { return Promise.all( Object.entries(fileIdsByIndex).map(([index, fileIds]) => { - return esClient.updateByQuery( - { - index, - refresh: true, - query: { - ids: { - values: Array.from(fileIds), + return esClient + .updateByQuery( + { + index, + refresh: true, + query: { + ids: { + values: Array.from(fileIds), + }, + }, + script: { + source: `ctx._source.file.Status = '${status}'`, + lang: 'painless', }, }, - script: { - source: `ctx._source.file.Status = '${status}'`, - lang: 'painless', - }, - }, - { signal: abortController.signal } - ); + { signal: abortController.signal } + ) + .catch((err) => { + Error.captureStackTrace(err); + throw err; + }); }) ); } diff --git a/x-pack/plugins/fleet/server/services/files/mocks.ts b/x-pack/plugins/fleet/server/services/files/mocks.ts index 35c276bf5cae7..23c0482b7e111 100644 --- a/x-pack/plugins/fleet/server/services/files/mocks.ts +++ b/x-pack/plugins/fleet/server/services/files/mocks.ts @@ -86,7 +86,7 @@ export const createFromHostEsSearchResponseMock = max_score: 0, hits: [ { - _index: '.fleet-files-foo-000001', + _index: '.fleet-fileds-fromhost-meta-foo-000001', _id: '123', _score: 1.0, _source: { diff --git a/x-pack/plugins/fleet/server/services/setup.test.ts b/x-pack/plugins/fleet/server/services/setup.test.ts index 7d98db879910b..15dccb15053ab 100644 --- a/x-pack/plugins/fleet/server/services/setup.test.ts +++ b/x-pack/plugins/fleet/server/services/setup.test.ts @@ -15,9 +15,7 @@ import { ensurePreconfiguredPackagesAndPolicies } from '.'; import { appContextService } from './app_context'; import { getInstallations } from './epm/packages'; import { upgradeManagedPackagePolicies } from './managed_package_policies'; -import { setupFleet, ensureFleetFileUploadIndices } from './setup'; - -import { ensureFileUploadWriteIndices } from './epm/elasticsearch/template/install'; +import { setupFleet } from './setup'; jest.mock('./preconfiguration'); jest.mock('./preconfiguration/outputs'); @@ -70,8 +68,6 @@ describe('setupFleet', () => { soClient.find.mockResolvedValue({ saved_objects: [] } as any); soClient.bulkGet.mockResolvedValue({ saved_objects: [] } as any); - - (ensureFileUploadWriteIndices as jest.Mock).mockResolvedValue({}); }); afterEach(async () => { @@ -138,12 +134,4 @@ describe('setupFleet', () => { ], }); }); - - it('should create agent file upload write indices', async () => { - await ensureFleetFileUploadIndices(soClient, esClient); - - expect((ensureFileUploadWriteIndices as jest.Mock).mock.calls[0][0].integrationNames).toEqual([ - 'elastic_agent', - ]); - }); }); diff --git a/x-pack/plugins/fleet/server/services/setup.ts b/x-pack/plugins/fleet/server/services/setup.ts index 90b76b8495e39..92d8f8fb37dda 100644 --- a/x-pack/plugins/fleet/server/services/setup.ts +++ b/x-pack/plugins/fleet/server/services/setup.ts @@ -12,11 +12,7 @@ import pMap from 'p-map'; import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common/constants'; -import { - AUTO_UPDATE_PACKAGES, - FILE_STORAGE_INTEGRATION_NAMES, - FLEET_ELASTIC_AGENT_PACKAGE, -} from '../../common/constants'; +import { AUTO_UPDATE_PACKAGES } from '../../common/constants'; import type { PreconfigurationError } from '../../common/constants'; import type { DefaultPackagesInstallationError, @@ -44,10 +40,7 @@ import { ensureDefaultEnrollmentAPIKeyForAgentPolicy } from './api_keys'; import { getRegistryUrl, settingsService } from '.'; import { awaitIfPending } from './setup_utils'; import { ensureFleetFinalPipelineIsInstalled } from './epm/elasticsearch/ingest_pipeline/install'; -import { - ensureDefaultComponentTemplates, - ensureFileUploadWriteIndices, -} from './epm/elasticsearch/template/install'; +import { ensureDefaultComponentTemplates } from './epm/elasticsearch/template/install'; import { getInstallations, reinstallPackageForInstallation } from './epm/packages'; import { isPackageInstalled } from './epm/packages/install'; import type { UpgradeManagedPackagePoliciesResult } from './managed_package_policies'; @@ -60,7 +53,6 @@ import { ensurePreconfiguredFleetServerHosts, getPreconfiguredFleetServerHostFromConfig, } from './preconfiguration/fleet_server_host'; -import { getInstallationsByName } from './epm/packages/get'; export interface SetupStatus { isInitialized: boolean; @@ -125,7 +117,6 @@ async function createSetupSideEffects( logger.debug('Setting up Fleet Elasticsearch assets'); await ensureFleetGlobalEsAssets(soClient, esClient); - await ensureFleetFileUploadIndices(soClient, esClient); // Ensure that required packages are always installed even if they're left out of the config const preconfiguredPackageNames = new Set(packages.map((pkg) => pkg.name)); @@ -207,32 +198,6 @@ async function createSetupSideEffects( }; } -/** - * Ensure ES assets shared by all Fleet index template are installed - */ -export async function ensureFleetFileUploadIndices( - soClient: SavedObjectsClientContract, - esClient: ElasticsearchClient -) { - const { diagnosticFileUploadEnabled } = appContextService.getExperimentalFeatures(); - if (!diagnosticFileUploadEnabled) return; - const logger = appContextService.getLogger(); - const installedFileUploadIntegrations = await getInstallationsByName({ - savedObjectsClient: soClient, - pkgNames: [...FILE_STORAGE_INTEGRATION_NAMES], - }); - - const integrationNames = installedFileUploadIntegrations.map(({ name }) => name); - if (!integrationNames.includes(FLEET_ELASTIC_AGENT_PACKAGE)) { - integrationNames.push(FLEET_ELASTIC_AGENT_PACKAGE); - } - logger.debug(`Ensuring file upload write indices for ${integrationNames}`); - return ensureFileUploadWriteIndices({ - esClient, - logger, - integrationNames, - }); -} /** * Ensure ES assets shared by all Fleet index template are installed */ diff --git a/x-pack/plugins/fleet/server/tasks/check_deleted_files_task.ts b/x-pack/plugins/fleet/server/tasks/check_deleted_files_task.ts index b7ebdd0748e9d..a7611d73cd313 100644 --- a/x-pack/plugins/fleet/server/tasks/check_deleted_files_task.ts +++ b/x-pack/plugins/fleet/server/tasks/check_deleted_files_task.ts @@ -72,6 +72,7 @@ export class CheckDeletedFilesTask { } this.wasStarted = true; + this.logger.info(`Started with interval of [${INTERVAL}] and timeout of [${TIMEOUT}]`); try { await taskManager.ensureScheduled({ @@ -85,7 +86,7 @@ export class CheckDeletedFilesTask { params: { version: VERSION }, }); } catch (e) { - this.logger.error(`Error scheduling task, received error: ${e}`); + this.logger.error(`Error scheduling task, received error: ${e.message}`, e); } }; @@ -104,19 +105,34 @@ export class CheckDeletedFilesTask { throwUnrecoverableError(new Error('Outdated task version')); } + this.logger.info(`[runTask()] started`); + + const endRun = (msg: string = '') => { + this.logger.info(`[runTask()] ended${msg ? ': ' + msg : ''}`); + }; + const [{ elasticsearch }] = await core.getStartServices(); const esClient = elasticsearch.client.asInternalUser; try { const readyFiles = await getFilesByStatus(esClient, this.abortController); - if (!readyFiles.length) return; + + if (!readyFiles.length) { + endRun('no files to process'); + return; + } const { fileIdsByIndex: deletedFileIdsByIndex, allFileIds: allDeletedFileIds } = await fileIdsWithoutChunksByIndex(esClient, this.abortController, readyFiles); - if (!allDeletedFileIds.size) return; + + if (!allDeletedFileIds.size) { + endRun('No files with deleted chunks'); + return; + } this.logger.info(`Attempting to update ${allDeletedFileIds.size} files to DELETED status`); - this.logger.debug(`Attempting to file ids: ${deletedFileIdsByIndex}`); + this.logger.debug(`Attempting to update file ids: ${deletedFileIdsByIndex}`); + const updatedFilesResponses = await updateFilesStatus( esClient, this.abortController, @@ -130,12 +146,16 @@ export class CheckDeletedFilesTask { this.logger.warn(`Failed to update ${failures.length} files to DELETED status`); this.logger.debug(`Failed to update files to DELETED status: ${failures}`); } + + endRun('success'); } catch (err) { if (err instanceof errors.RequestAbortedError) { this.logger.warn(`request aborted due to timeout: ${err}`); + endRun(); return; } this.logger.error(err); + endRun('error'); } }; } diff --git a/x-pack/plugins/infra/common/alerting/logs/log_threshold/query_helpers.ts b/x-pack/plugins/infra/common/alerting/logs/log_threshold/query_helpers.ts index a88d8fa767654..e2e656fe1831c 100644 --- a/x-pack/plugins/infra/common/alerting/logs/log_threshold/query_helpers.ts +++ b/x-pack/plugins/infra/common/alerting/logs/log_threshold/query_helpers.ts @@ -60,7 +60,9 @@ export const buildFiltersFromCriteria = ( }, }; - return { rangeFilter, groupedRangeFilter, mustFilters, mustNotFilters }; + const mustFiltersFields = positiveCriteria.map((criterion) => criterion.field); + + return { rangeFilter, groupedRangeFilter, mustFilters, mustNotFilters, mustFiltersFields }; }; const buildFiltersForCriteria = (criteria: CountCriteria) => { diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/components/explain_log_rate_spike.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/components/explain_log_rate_spike.tsx index e0e3205ac29af..b51c9beae3836 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/components/explain_log_rate_spike.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/components/explain_log_rate_spike.tsx @@ -28,7 +28,6 @@ import { useKibanaContextForPlugin } from '../../../../../hooks/use_kibana'; import { Comparator, CountRuleParams, - hasGroupBy, isRatioRuleParams, PartialRuleParams, ruleParamsRT, @@ -76,7 +75,9 @@ export const ExplainLogRateSpikes: FC { const esSearchRequest = getESQueryForLogSpike( validatedParams as CountRuleParams, - timestampField + timestampField, + alert, + rule.params.groupBy ) as QueryDslQueryContainer; if (esSearchRequest) { @@ -88,7 +89,6 @@ export const ExplainLogRateSpikes: FC & { criteria: CountCriteria }, - timestampField: string + timestampField: string, + alert: TopAlert>, + groupBy?: string[] | undefined ): object => { - const { mustFilters, mustNotFilters } = buildFiltersFromCriteria(params, timestampField); + const { mustFilters, mustNotFilters, mustFiltersFields } = buildFiltersFromCriteria( + params, + timestampField + ); + + const groupByFilters = groupBy + ? groupBy + .filter((groupByField) => !mustFiltersFields.includes(groupByField)) + .map((groupByField) => { + const groupByValue = get( + alert.fields[ALERT_CONTEXT], + ['groupByKeys', ...groupByField.split('.')], + null + ); + return groupByValue ? { term: { [groupByField]: { value: groupByValue } } } : null; + }) + .filter((groupByFilter) => groupByFilter) + : []; const query = { bool: { - filter: mustFilters, + filter: [...mustFilters, ...groupByFilters], ...(mustNotFilters.length > 0 && { must_not: mustNotFilters }), }, }; diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx new file mode 100644 index 0000000000000..0eb71d6f6e1d7 --- /dev/null +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx @@ -0,0 +1,129 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect, useState } from 'react'; +import { EuiFlyout, EuiLoadingSpinner, EuiOverlayMask } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { Provider } from 'react-redux'; +import { PreloadedState } from '@reduxjs/toolkit'; +import { css } from '@emotion/react'; +import type { CoreStart } from '@kbn/core/public'; +import type { LensPluginStartDependencies } from '../../../plugin'; +import { + makeConfigureStore, + LensRootStore, + LensAppState, + LensState, +} from '../../../state_management'; +import { getPreloadedState } from '../../../state_management/lens_slice'; + +import type { DatasourceMap, VisualizationMap } from '../../../types'; +import { + LensEditConfigurationFlyout, + type EditConfigPanelProps, +} from './lens_configuration_flyout'; +import type { LensAppServices } from '../../types'; + +export type EditLensConfigurationProps = Omit< + EditConfigPanelProps, + 'startDependencies' | 'coreStart' | 'visualizationMap' | 'datasourceMap' +>; + +function LoadingSpinnerWithOverlay() { + return ( + + + + ); +} + +export function getEditLensConfiguration( + coreStart: CoreStart, + startDependencies: LensPluginStartDependencies, + visualizationMap?: VisualizationMap, + datasourceMap?: DatasourceMap +) { + return ({ + attributes, + dataView, + updateAll, + setIsFlyoutVisible, + datasourceId, + adaptersTables, + }: EditLensConfigurationProps) => { + const [lensServices, setLensServices] = useState(); + useEffect(() => { + async function loadLensService() { + const { getLensServices, getLensAttributeService } = await import( + '../../../async_services' + ); + const lensServicesT = await getLensServices( + coreStart, + startDependencies, + getLensAttributeService(coreStart, startDependencies) + ); + + setLensServices(lensServicesT); + } + loadLensService(); + }, []); + + if (!lensServices || !datasourceMap || !visualizationMap || !dataView.id) { + return ; + } + const datasourceState = attributes.state.datasourceStates[datasourceId]; + const storeDeps = { + lensServices, + datasourceMap, + visualizationMap, + initialContext: + datasourceState && 'initialContext' in datasourceState + ? datasourceState.initialContext + : undefined, + }; + const lensStore: LensRootStore = makeConfigureStore(storeDeps, { + lens: getPreloadedState(storeDeps) as LensAppState, + } as unknown as PreloadedState); + const closeFlyout = () => { + setIsFlyoutVisible?.(false); + }; + + const configPanelProps = { + attributes, + dataView, + updateAll, + setIsFlyoutVisible, + datasourceId, + adaptersTables, + coreStart, + startDependencies, + visualizationMap, + datasourceMap, + }; + + return ( + + + + + + ); + }; +} diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx new file mode 100644 index 0000000000000..dc05ff1577382 --- /dev/null +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx @@ -0,0 +1,433 @@ +/* + * Copyright 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 { EuiFlyoutBody } from '@elastic/eui'; +import { mountWithProvider } from '../../../mocks'; +import type { Query, AggregateQuery } from '@kbn/es-query'; +import type { DataView } from '@kbn/data-views-plugin/public'; +import { coreMock } from '@kbn/core/public/mocks'; +import { + mockVisualizationMap, + mockDatasourceMap, + mockStoreDeps, + mockDataPlugin, +} from '../../../mocks'; +import type { LensPluginStartDependencies } from '../../../plugin'; +import { createMockStartDependencies } from '../../../editor_frame_service/mocks'; +import type { TypedLensByValueInput } from '../../../embeddable/embeddable_component'; +import { VisualizationToolbar } from '../../../editor_frame_service/editor_frame/workspace_panel'; +import { ConfigPanelWrapper } from '../../../editor_frame_service/editor_frame/config_panel/config_panel'; +import { + LensEditConfigurationFlyout, + type EditConfigPanelProps, +} from './lens_configuration_flyout'; + +let container: HTMLDivElement | undefined; + +beforeEach(() => { + container = document.createElement('div'); + container.id = 'lensContainer'; + document.body.appendChild(container); +}); + +afterEach(() => { + if (container && container.parentNode) { + container.parentNode.removeChild(container); + } + + container = undefined; +}); + +describe('LensEditConfigurationFlyout', () => { + const mockStartDependencies = + createMockStartDependencies() as unknown as LensPluginStartDependencies; + const data = mockDataPlugin(); + (data.query.timefilter.timefilter.getTime as jest.Mock).mockReturnValue({ + from: 'now-2m', + to: 'now', + }); + const startDependencies = { + ...mockStartDependencies, + data, + }; + + function prepareAndMountComponent( + props: ReturnType, + query?: Query | AggregateQuery + ) { + return mountWithProvider( + , + { + preloadedState: { + datasourceStates: { + testDatasource: { + isLoading: false, + state: 'state', + }, + }, + activeDatasourceId: 'testDatasource', + query: query as Query, + }, + storeDeps: mockStoreDeps({ + datasourceMap: props.datasourceMap, + visualizationMap: props.visualizationMap, + }), + }, + { + attachTo: container, + } + ); + } + + function getDefaultProps( + { datasourceMap = mockDatasourceMap(), visualizationMap = mockVisualizationMap() } = { + datasourceMap: mockDatasourceMap(), + visualizationMap: mockVisualizationMap(), + } + ) { + const lensAttributes = { + title: 'test', + visualizationType: 'testVis', + state: { + datasourceStates: { + testDatasource: {}, + }, + visualization: {}, + filters: [], + query: { + language: 'lucene', + query: '', + }, + }, + filters: [], + query: { + language: 'lucene', + query: '', + }, + references: [], + } as unknown as TypedLensByValueInput['attributes']; + + const dataView = { id: 'index1', isPersisted: () => true } as unknown as DataView; + return { + attributes: lensAttributes, + dataView, + updateAll: jest.fn(), + coreStart: coreMock.createStart(), + startDependencies, + visualizationMap, + datasourceMap, + setIsFlyoutVisible: jest.fn(), + datasourceId: 'testDatasource', + } as unknown as EditConfigPanelProps; + } + + it('should call the setIsFlyout callback if collapse button is clicked', async () => { + const setIsFlyoutVisibleSpy = jest.fn(); + const props = getDefaultProps(); + const newProps = { + ...props, + setIsFlyoutVisible: setIsFlyoutVisibleSpy, + }; + const { instance } = await prepareAndMountComponent(newProps); + expect(instance.find(EuiFlyoutBody).exists()).toBe(true); + instance.find('[data-test-subj="collapseFlyoutButton"]').at(1).simulate('click'); + expect(setIsFlyoutVisibleSpy).toHaveBeenCalled(); + }); + + it('should compute the frame public api correctly', async () => { + const props = getDefaultProps(); + const { instance } = await prepareAndMountComponent(props); + expect(instance.find(ConfigPanelWrapper).exists()).toBe(true); + expect(instance.find(VisualizationToolbar).exists()).toBe(true); + expect(instance.find(VisualizationToolbar).prop('framePublicAPI')).toMatchInlineSnapshot(` + Object { + "activeData": Object {}, + "dataViews": Object { + "indexPatternRefs": Array [], + "indexPatterns": Object { + "index1": Object { + "id": "index1", + "isPersisted": [Function], + }, + }, + }, + "datasourceLayers": Object { + "a": Object { + "datasourceId": "testDatasource", + "getFilters": [MockFunction], + "getMaxPossibleNumValues": [MockFunction], + "getOperationForColumnId": [MockFunction], + "getSourceId": [MockFunction], + "getTableSpec": [MockFunction], + "getVisualDefaults": [MockFunction], + "hasDefaultTimeField": [MockFunction], + "isTextBasedLanguage": [MockFunction] { + "calls": Array [ + Array [], + Array [], + ], + "results": Array [ + Object { + "type": "return", + "value": false, + }, + Object { + "type": "return", + "value": false, + }, + ], + }, + }, + }, + "dateRange": Object { + "fromDate": "2021-01-10T04:00:00.000Z", + "toDate": "2021-01-10T08:00:00.000Z", + }, + } + `); + }); + + it('should compute the activeVisualization correctly', async () => { + const props = getDefaultProps(); + const { instance } = await prepareAndMountComponent(props); + expect(instance.find(VisualizationToolbar).prop('activeVisualization')).toMatchInlineSnapshot(` + Object { + "appendLayer": [MockFunction], + "clearLayer": [MockFunction], + "getConfiguration": [MockFunction] { + "calls": Array [ + Array [ + Object { + "frame": Object { + "activeData": Object {}, + "dataViews": Object { + "indexPatternRefs": Array [], + "indexPatterns": Object { + "index1": Object { + "id": "index1", + "isPersisted": [Function], + }, + }, + }, + "datasourceLayers": Object { + "a": Object { + "datasourceId": "testDatasource", + "getFilters": [MockFunction], + "getMaxPossibleNumValues": [MockFunction], + "getOperationForColumnId": [MockFunction], + "getSourceId": [MockFunction], + "getTableSpec": [MockFunction], + "getVisualDefaults": [MockFunction], + "hasDefaultTimeField": [MockFunction], + "isTextBasedLanguage": [MockFunction] { + "calls": Array [ + Array [], + Array [], + ], + "results": Array [ + Object { + "type": "return", + "value": false, + }, + Object { + "type": "return", + "value": false, + }, + ], + }, + }, + }, + "dateRange": Object { + "fromDate": "2021-01-10T04:00:00.000Z", + "toDate": "2021-01-10T08:00:00.000Z", + }, + }, + "layerId": "layer1", + "state": Object {}, + }, + ], + Array [ + Object { + "frame": Object { + "activeData": Object {}, + "dataViews": Object { + "indexPatternRefs": Array [], + "indexPatterns": Object { + "index1": Object { + "id": "index1", + "isPersisted": [Function], + }, + }, + }, + "datasourceLayers": Object { + "a": Object { + "datasourceId": "testDatasource", + "getFilters": [MockFunction], + "getMaxPossibleNumValues": [MockFunction], + "getOperationForColumnId": [MockFunction], + "getSourceId": [MockFunction], + "getTableSpec": [MockFunction], + "getVisualDefaults": [MockFunction], + "hasDefaultTimeField": [MockFunction], + "isTextBasedLanguage": [MockFunction] { + "calls": Array [ + Array [], + Array [], + ], + "results": Array [ + Object { + "type": "return", + "value": false, + }, + Object { + "type": "return", + "value": false, + }, + ], + }, + }, + }, + "dateRange": Object { + "fromDate": "2021-01-10T04:00:00.000Z", + "toDate": "2021-01-10T08:00:00.000Z", + }, + }, + "layerId": "layer1", + "state": Object {}, + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Object { + "groups": Array [ + Object { + "accessors": Array [], + "dataTestSubj": "mockVisA", + "filterOperations": [MockFunction], + "groupId": "a", + "groupLabel": "a", + "layerId": "layer1", + "supportsMoreColumns": true, + }, + ], + }, + }, + Object { + "type": "return", + "value": Object { + "groups": Array [ + Object { + "accessors": Array [], + "dataTestSubj": "mockVisA", + "filterOperations": [MockFunction], + "groupId": "a", + "groupLabel": "a", + "layerId": "layer1", + "supportsMoreColumns": true, + }, + ], + }, + }, + ], + }, + "getDescription": [MockFunction] { + "calls": Array [ + Array [ + Object {}, + ], + Array [ + Object {}, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Object { + "label": "", + }, + }, + Object { + "type": "return", + "value": Object { + "label": "", + }, + }, + ], + }, + "getLayerIds": [MockFunction] { + "calls": Array [ + Array [ + Object {}, + ], + Array [ + Object {}, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Array [ + "layer1", + ], + }, + Object { + "type": "return", + "value": Array [ + "layer1", + ], + }, + ], + }, + "getLayerType": [MockFunction] { + "calls": Array [ + Array [ + "layer1", + Object {}, + ], + Array [ + "layer1", + Object {}, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": "data", + }, + Object { + "type": "return", + "value": "data", + }, + ], + }, + "getRenderEventCounters": [MockFunction], + "getSuggestions": [MockFunction], + "getSupportedLayers": [MockFunction], + "getVisualizationTypeId": [MockFunction], + "id": "testVis", + "initialize": [MockFunction], + "removeDimension": [MockFunction], + "removeLayer": [MockFunction], + "renderDimensionEditor": [MockFunction], + "setDimension": [MockFunction], + "switchVisualizationType": [MockFunction], + "toExpression": [MockFunction], + "toPreviewExpression": [MockFunction], + "visualizationTypes": Array [ + Object { + "groupLabel": "testVisGroup", + "icon": "empty", + "id": "testVis", + "label": "TEST", + }, + ], + } + `); + }); +}); diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx new file mode 100644 index 0000000000000..9fe486c58048a --- /dev/null +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -0,0 +1,174 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; +import { + EuiButtonEmpty, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiSpacer, + EuiFlexGroup, + EuiFlexItem, + useEuiTheme, + EuiCallOut, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { css } from '@emotion/react'; +import type { CoreStart } from '@kbn/core/public'; +import type { Datatable } from '@kbn/expressions-plugin/public'; +import type { DataView } from '@kbn/data-views-plugin/public'; +import { getResolvedDateRange } from '../../../utils'; +import type { LensPluginStartDependencies } from '../../../plugin'; +import { + DataViewsState, + useLensDispatch, + updateStateFromSuggestion, +} from '../../../state_management'; +import { VisualizationToolbar } from '../../../editor_frame_service/editor_frame/workspace_panel'; + +import type { DatasourceMap, VisualizationMap, DatasourceLayers } from '../../../types'; +import type { TypedLensByValueInput } from '../../../embeddable/embeddable_component'; +import { ConfigPanelWrapper } from '../../../editor_frame_service/editor_frame/config_panel/config_panel'; + +export interface EditConfigPanelProps { + attributes: TypedLensByValueInput['attributes']; + dataView: DataView; + updateAll: (datasourceState: unknown, visualizationState: unknown) => void; + coreStart: CoreStart; + startDependencies: LensPluginStartDependencies; + visualizationMap: VisualizationMap; + datasourceMap: DatasourceMap; + setIsFlyoutVisible?: (flag: boolean) => void; + datasourceId: 'formBased' | 'textBased'; + adaptersTables?: Record; +} + +export function LensEditConfigurationFlyout({ + attributes, + dataView, + coreStart, + startDependencies, + visualizationMap, + datasourceMap, + datasourceId, + updateAll, + setIsFlyoutVisible, + adaptersTables, +}: EditConfigPanelProps) { + const currentDataViewId = dataView.id ?? ''; + const datasourceState = attributes.state.datasourceStates[datasourceId]; + const activeVisualization = visualizationMap[attributes.visualizationType]; + const activeDatasource = datasourceMap[datasourceId]; + const dispatchLens = useLensDispatch(); + const { euiTheme } = useEuiTheme(); + const dataViews = useMemo(() => { + return { + indexPatterns: { + [currentDataViewId]: dataView, + }, + indexPatternRefs: [], + } as unknown as DataViewsState; + }, [currentDataViewId, dataView]); + dispatchLens( + updateStateFromSuggestion({ + newDatasourceId: datasourceId, + visualizationId: activeVisualization.id, + visualizationState: attributes.state.visualization, + datasourceState, + dataViews, + }) + ); + + const datasourceLayers: DatasourceLayers = useMemo(() => { + return {}; + }, []); + const activeData: Record = useMemo(() => { + return {}; + }, []); + const layers = activeDatasource.getLayers(datasourceState); + layers.forEach((layer) => { + datasourceLayers[layer] = datasourceMap[datasourceId].getPublicAPI({ + state: datasourceState, + layerId: layer, + indexPatterns: dataViews.indexPatterns, + }); + if (adaptersTables) { + activeData[layer] = Object.values(adaptersTables)[0]; + } + }); + + const dateRange = getResolvedDateRange(startDependencies.data.query.timefilter.timefilter); + const framePublicAPI = useMemo(() => { + return { + activeData, + dataViews, + datasourceLayers, + dateRange, + }; + }, [activeData, dataViews, datasourceLayers, dateRange]); + + const closeFlyout = () => { + setIsFlyoutVisible?.(false); + }; + + const layerPanelsProps = { + framePublicAPI, + datasourceMap, + visualizationMap, + core: coreStart, + dataViews: startDependencies.dataViews, + uiActions: startDependencies.uiActions, + hideLayerHeader: true, + onUpdateStateCb: updateAll, + }; + return ( + <> + + + + + + + + + + + + + + + + + + ); +} diff --git a/x-pack/plugins/lens/public/async_services.ts b/x-pack/plugins/lens/public/async_services.ts index 38a904c5617c9..d4c6fe5be8dcd 100644 --- a/x-pack/plugins/lens/public/async_services.ts +++ b/x-pack/plugins/lens/public/async_services.ts @@ -30,6 +30,7 @@ export * from './visualizations/gauge/gauge_visualization'; export * from './visualizations/gauge'; export * from './visualizations/tagcloud/tagcloud_visualization'; export * from './visualizations/tagcloud'; +export { getEditLensConfiguration } from './app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration'; export * from './datasources/form_based/form_based'; export { getTextBasedDatasource } from './datasources/text_based/text_based_languages'; diff --git a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.test.ts b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.test.ts index 42b405c939d3c..d12505e93f07a 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.test.ts +++ b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.test.ts @@ -410,6 +410,22 @@ describe('Textbased Data Source', () => { ); expect(suggestions[0].state).toEqual({ ...state, + fieldList: [ + { + id: 'newid', + meta: { + type: 'number', + }, + name: 'bytes', + }, + { + id: 'newid', + meta: { + type: 'string', + }, + name: 'dest', + }, + ], layers: { newid: { allColumns: [ diff --git a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx index 573137da1ebc2..653a3f30e1b44 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx +++ b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx @@ -122,6 +122,14 @@ export function getTextBasedDatasource({ const query = context.query; const updatedState = { ...state, + fieldList: + newColumns?.map((c) => { + return { + id: c.columnId, + name: c.fieldName, + meta: c.meta, + }; + }) ?? [], layers: { ...state.layers, [newLayerId]: { diff --git a/x-pack/plugins/lens/public/datasources/text_based/types.ts b/x-pack/plugins/lens/public/datasources/text_based/types.ts index 0594fdcf2fbc2..544996c904b77 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/types.ts +++ b/x-pack/plugins/lens/public/datasources/text_based/types.ts @@ -31,12 +31,12 @@ export interface TextBasedLayer { export interface TextBasedPersistedState { layers: Record; + initialContext?: VisualizeFieldContext | VisualizeEditorContext; } export type TextBasedPrivateState = TextBasedPersistedState & { indexPatternRefs: IndexPatternRef[]; fieldList: DatatableColumn[]; - initialContext?: VisualizeFieldContext | VisualizeEditorContext; }; export interface IndexPatternRef { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx index 78f7246c52e6d..f9e09143d3e43 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx @@ -170,13 +170,19 @@ describe('ConfigPanel', () => { it('allow datasources and visualizations to use setters', async () => { const props = getDefaultProps(); - const { instance, lensStore } = await prepareAndMountComponent(props); + const onUpdateCbSpy = jest.fn(); + const newProps = { + ...props, + onUpdateStateCb: onUpdateCbSpy, + }; + const { instance, lensStore } = await prepareAndMountComponent(newProps); const { updateDatasource, updateAll } = instance.find(LayerPanel).props(); const updater = () => 'updated'; updateDatasource('testDatasource', updater); await waitMs(0); expect(lensStore.dispatch).toHaveBeenCalledTimes(1); + expect(onUpdateCbSpy).toHaveBeenCalled(); expect( (lensStore.dispatch as jest.Mock).mock.calls[0][0].payload.updater( props.datasourceStates.testDatasource.state @@ -184,6 +190,7 @@ describe('ConfigPanel', () => { ).toEqual('updated'); updateAll('testDatasource', updater, props.visualizationState); + expect(onUpdateCbSpy).toHaveBeenCalled(); // wait for one tick so async updater has a chance to trigger await waitMs(0); expect(lensStore.dispatch).toHaveBeenCalledTimes(2); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx index af1549e00cc30..571fc5194d5e3 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx @@ -6,6 +6,7 @@ */ import React, { useMemo, memo, useCallback } from 'react'; +import { useStore } from 'react-redux'; import { EuiForm } from '@elastic/eui'; import { ActionExecutionContext } from '@kbn/ui-actions-plugin/public'; import { isOfAggregateQueryType } from '@kbn/es-query'; @@ -52,7 +53,8 @@ export function LayerPanels( activeVisualization: Visualization; } ) { - const { activeVisualization, datasourceMap, indexPatternService } = props; + const lensStore = useStore(); + const { activeVisualization, datasourceMap, indexPatternService, onUpdateStateCb } = props; const { activeDatasourceId, visualization, datasourceStates, query } = useLensSelector( (state) => state.lens ); @@ -74,8 +76,12 @@ export function LayerPanels( newState, }) ); + if (onUpdateStateCb && activeDatasourceId) { + const dsState = datasourceStates[activeDatasourceId].state; + onUpdateStateCb?.(dsState, newState); + } }, - [activeVisualization, dispatchLens] + [activeDatasourceId, activeVisualization.id, datasourceStates, dispatchLens, onUpdateStateCb] ); const updateDatasource = useMemo( () => @@ -90,9 +96,10 @@ export function LayerPanels( dontSyncLinkedDimensions, }) ); + onUpdateStateCb?.(newState, visualization.state); } }, - [dispatchLens] + [dispatchLens, onUpdateStateCb, visualization.state] ); const updateDatasourceAsync = useMemo( () => (datasourceId: string | undefined, newState: unknown) => { @@ -147,9 +154,10 @@ export function LayerPanels( }, }) ); + onUpdateStateCb?.(newDatasourceState, newVisualizationState); }, 0); }, - [dispatchLens] + [dispatchLens, onUpdateStateCb] ); const toggleFullscreen = useMemo( @@ -213,20 +221,21 @@ export function LayerPanels( visualizationId?: string; layerId?: string; }) => { - const indexPatterns = await props.indexPatternService.ensureIndexPattern({ + const indexPatterns = await props.indexPatternService?.ensureIndexPattern({ id: indexPatternId, cache: props.framePublicAPI.dataViews.indexPatterns, }); - - dispatchLens( - changeIndexPattern({ - indexPatternId, - datasourceIds: datasourceId ? [datasourceId] : [], - visualizationIds: visualizationId ? [visualizationId] : [], - layerId, - dataViews: { indexPatterns }, - }) - ); + if (indexPatterns) { + dispatchLens( + changeIndexPattern({ + indexPatternId, + datasourceIds: datasourceId ? [datasourceId] : [], + visualizationIds: visualizationId ? [visualizationId] : [], + layerId, + dataViews: { indexPatterns }, + }) + ); + } }, [dispatchLens, props.framePublicAPI.dataViews.indexPatterns, props.indexPatternService] ); @@ -262,6 +271,7 @@ export function LayerPanels( updateVisualization={setVisualizationState} updateDatasource={updateDatasource} updateDatasourceAsync={updateDatasourceAsync} + displayLayerSettings={!props.hideLayerHeader} onChangeIndexPattern={(args) => { onChangeIndexPattern(args); const layersToRemove = @@ -307,6 +317,13 @@ export function LayerPanels( const datasourcePublicAPI = props.framePublicAPI.datasourceLayers?.[layerId]; const datasourceId = datasourcePublicAPI?.datasourceId; dispatchLens(removeDimension({ ...dimensionProps, datasourceId })); + if (datasourceId && onUpdateStateCb) { + const newState = lensStore.getState().lens; + onUpdateStateCb( + newState.datasourceStates[datasourceId].state, + newState.visualization.state + ); + } }} toggleFullscreen={toggleFullscreen} indexPatternService={indexPatternService} @@ -336,19 +353,21 @@ export function LayerPanels( indexPatternId = dataView.id; } - const newIndexPatterns = await indexPatternService.ensureIndexPattern({ + const newIndexPatterns = await indexPatternService?.ensureIndexPattern({ id: indexPatternId, cache: props.framePublicAPI.dataViews.indexPatterns, }); - dispatchLens( - changeIndexPattern({ - dataViews: { indexPatterns: newIndexPatterns }, - datasourceIds: Object.keys(datasourceStates), - visualizationIds: visualization.activeId ? [visualization.activeId] : [], - indexPatternId, - }) - ); + if (newIndexPatterns) { + dispatchLens( + changeIndexPattern({ + dataViews: { indexPatterns: newIndexPatterns }, + datasourceIds: Object.keys(datasourceStates), + visualizationIds: visualization.activeId ? [visualization.activeId] : [], + indexPatternId, + }) + ); + } }, registerLibraryAnnotationGroup: (groupInfo) => dispatchLens(registerLibraryAnnotationGroup(groupInfo)), diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx index 248681717b082..2b8568bd12908 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx @@ -11,6 +11,7 @@ import { EuiFormRow } from '@elastic/eui'; import { ChildDragDropProvider, DragDrop } from '@kbn/dom-drag-drop'; import { FramePublicAPI, Visualization, VisualizationConfigProps } from '../../../types'; import { LayerPanel } from './layer_panel'; +import { LayerActions } from './layer_actions'; import { coreMock } from '@kbn/core/public/mocks'; import { generateId } from '../../../id_generator'; import { @@ -116,6 +117,7 @@ describe('LayerPanel', () => { onChangeIndexPattern: jest.fn(), indexPatternService: createIndexPatternServiceMock(), getUserMessages: () => [], + displayLayerSettings: true, }; } @@ -203,6 +205,13 @@ describe('LayerPanel', () => { expect(optionalLabel.text()).toEqual('Optional'); }); + it('should hide the layer actions if displayLayerSettings is set to false', async () => { + const { instance } = await mountWithProvider( + + ); + expect(instance.find(LayerActions).exists()).toBe(false); + }); + it('should render the group with a way to add a new column', async () => { mockVisualization.getConfiguration.mockReturnValue({ groups: [ 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 bb90c82b235e6..84c0b18d30c5a 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 @@ -87,8 +87,9 @@ export function LayerPanel( datasourceId?: string; visualizationId?: string; }) => void; - indexPatternService: IndexPatternServiceAPI; - getUserMessages: UserMessagesGetter; + indexPatternService?: IndexPatternServiceAPI; + getUserMessages?: UserMessagesGetter; + displayLayerSettings: boolean; } ) { const [activeDimension, setActiveDimension] = useState( @@ -418,17 +419,20 @@ export function LayerPanel( activeVisualization={activeVisualization} />
- - -
- + {props.displayLayerSettings && ( + + +
+ + )} - {(layerDatasource || activeVisualization.renderLayerPanel) && } - {layerDatasource && ( + {props.indexPatternService && + (layerDatasource || activeVisualization.renderLayerPanel) && } + {layerDatasource && props.indexPatternService && ( { const { columnId } = accessorConfig; - const messages = props.getUserMessages('dimensionButton', { - dimensionId: columnId, - }); + const messages = + props?.getUserMessages?.('dimensionButton', { + dimensionId: columnId, + }) ?? []; return ( void; } export interface LayerPanelProps { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/index.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/index.ts index c3ba019ca68ad..9f51ea611e9b8 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/index.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/index.ts @@ -6,3 +6,4 @@ */ export { WorkspacePanel } from './workspace_panel'; +export { VisualizationToolbar } from './workspace_panel_wrapper'; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.test.tsx index 42735bde405c4..700fe7f96bf84 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.test.tsx @@ -65,7 +65,13 @@ describe('workspace_panel_wrapper', () => { isFullscreen={false} lensInspector={{} as unknown as LensInspector} getUserMessages={() => []} - /> + />, + { + preloadedState: { + visualization: { activeId: 'myVis', state: visState }, + datasourceStates: {}, + }, + } ); expect(renderToolbarMock).toHaveBeenCalledWith(expect.any(Element), { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx index 6b61e4dd374c5..064b268209aea 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx @@ -16,6 +16,7 @@ import { FramePublicAPI, UserMessagesGetter, VisualizationMap, + Visualization, } from '../../../types'; import { DONT_CLOSE_DIMENSION_CONTAINER_ON_CLICK_CLASS } from '../../../utils'; import { NativeRenderer } from '../../../native_renderer'; @@ -49,6 +50,52 @@ export interface WorkspacePanelWrapperProps { getUserMessages: UserMessagesGetter; } +export function VisualizationToolbar(props: { + activeVisualization: Visualization | null; + framePublicAPI: FramePublicAPI; + onUpdateStateCb?: (datasourceState: unknown, visualizationState: unknown) => void; +}) { + const dispatchLens = useLensDispatch(); + const { activeDatasourceId, visualization, datasourceStates } = useLensSelector( + (state) => state.lens + ); + const setVisualizationState = useCallback( + (newState: unknown) => { + if (!props.activeVisualization) { + return; + } + dispatchLens( + updateVisualizationState({ + visualizationId: props.activeVisualization.id, + newState, + }) + ); + if (activeDatasourceId && props.onUpdateStateCb) { + const dsState = datasourceStates[activeDatasourceId].state; + props.onUpdateStateCb?.(dsState, newState); + } + }, + [activeDatasourceId, datasourceStates, dispatchLens, props] + ); + + return ( + <> + {props.activeVisualization && props.activeVisualization.renderToolbar && ( + + + + )} + + ); +} + export function WorkspacePanelWrapper({ children, framePublicAPI, @@ -65,21 +112,6 @@ export function WorkspacePanelWrapper({ const autoApplyEnabled = useLensSelector(selectAutoApplyEnabled); const activeVisualization = visualizationId ? visualizationMap[visualizationId] : null; - const setVisualizationState = useCallback( - (newState: unknown) => { - if (!activeVisualization) { - return; - } - dispatchLens( - updateVisualizationState({ - visualizationId: activeVisualization.id, - newState, - }) - ); - }, - [dispatchLens, activeVisualization] - ); - const userMessages = getUserMessages('toolbar'); return ( @@ -116,19 +148,10 @@ export function WorkspacePanelWrapper({ framePublicAPI={framePublicAPI} /> - - {activeVisualization && activeVisualization.renderToolbar && ( - - - - )} + )} diff --git a/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx b/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx index edccc071a57d1..4513e0f4bffd4 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx @@ -10,6 +10,8 @@ import { ExpressionsSetup, ExpressionsStart } from '@kbn/expressions-plugin/publ import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks'; import { expressionsPluginMock } from '@kbn/expressions-plugin/public/mocks'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; +import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { EditorFrameSetupPlugins, EditorFrameStartPlugins } from './service'; @@ -57,5 +59,7 @@ export function createMockStartDependencies() { embeddable: embeddablePluginMock.createStartContract(), expressions: expressionsPluginMock.createStartContract(), charts: chartPluginMock.createStartContract(), + uiActions: uiActionsPluginMock.createStartContract(), + dataViews: dataViewPluginMocks.createStartContract(), } as unknown as MockedStartDependencies; } diff --git a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx index 943e87c9c00c2..67076fb9c9200 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx @@ -23,6 +23,7 @@ import { import type { LensByReferenceInput, LensByValueInput } from './embeddable'; import type { Document } from '../persistence'; import type { FormBasedPersistedState } from '../datasources/form_based/types'; +import type { TextBasedPersistedState } from '../datasources/text_based/types'; import type { XYState } from '../visualizations/xy/types'; import type { PieVisualizationState, @@ -45,6 +46,7 @@ type LensAttributes = Omit< state: Omit & { datasourceStates: { formBased: FormBasedPersistedState; + textBased?: TextBasedPersistedState; }; visualization: TVisState; }; diff --git a/x-pack/plugins/lens/public/mocks/lens_plugin_mock.tsx b/x-pack/plugins/lens/public/mocks/lens_plugin_mock.tsx index cbf310cb2f50a..f526e46d8f5ec 100644 --- a/x-pack/plugins/lens/public/mocks/lens_plugin_mock.tsx +++ b/x-pack/plugins/lens/public/mocks/lens_plugin_mock.tsx @@ -21,6 +21,7 @@ export const lensPluginMock = { SaveModalComponent: jest.fn(() => { return Lens Save Modal Component; }), + EditLensConfigPanelApi: jest.fn().mockResolvedValue(Lens Config Panel Component), canUseEditor: jest.fn(() => true), navigateToPrefilledEditor: jest.fn(), getXyVisTypes: jest diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index b207f07266ae8..1a195183142c3 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -126,6 +126,7 @@ import { type LensAppLocator, LensAppLocatorDefinition } from '../common/locator import { downloadCsvShareProvider } from './app_plugin/csv_download_provider/csv_download_provider'; import { CONTENT_ID, LATEST_VERSION } from '../common/content_management'; +import type { EditLensConfigurationProps } from './app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration'; export interface LensPluginSetupDependencies { urlForwarding: UrlForwardingSetup; @@ -215,6 +216,14 @@ export interface LensPublicStart { * @experimental */ SaveModalComponent: React.ComponentType>; + /** + * React component which can be used to embed a Lens Visualization Config Panel Component. + * + * This API might undergo breaking changes even in minor versions. + * + * @experimental + */ + EditLensConfigPanelApi: () => Promise; /** * Method which navigates to the Lens editor, loading the state specified by the `input` parameter. * See `x-pack/examples/embedded_lens_example` for exemplary usage. @@ -252,6 +261,8 @@ export interface LensPublicStart { }>; } +export type EditLensConfigPanelComponent = React.ComponentType; + export type LensSuggestionsApi = ( context: VisualizeFieldContext | VisualizeEditorContext, dataViews: DataView, @@ -649,6 +660,17 @@ export class LensPlugin { }, }; }, + EditLensConfigPanelApi: async () => { + const { getEditLensConfiguration } = await import('./async_services'); + if (!this.editorFrameService) { + this.initDependenciesForApi(); + } + const [visualizationMap, datasourceMap] = await Promise.all([ + this.editorFrameService!.loadVisualizations(), + this.editorFrameService!.loadDatasources(), + ]); + return getEditLensConfiguration(core, startDependencies, visualizationMap, datasourceMap); + }, }; } diff --git a/x-pack/plugins/lens/public/state_management/index.ts b/x-pack/plugins/lens/public/state_management/index.ts index 9a9a4005714aa..f4b333e25c815 100644 --- a/x-pack/plugins/lens/public/state_management/index.ts +++ b/x-pack/plugins/lens/public/state_management/index.ts @@ -35,6 +35,7 @@ export const { submitSuggestion, switchDatasource, switchAndCleanDatasource, + updateStateFromSuggestion, updateIndexPatterns, setToggleFullscreen, initEmpty, diff --git a/x-pack/plugins/lens/public/state_management/lens_slice.test.ts b/x-pack/plugins/lens/public/state_management/lens_slice.test.ts index c69931837b3aa..0371d5564d503 100644 --- a/x-pack/plugins/lens/public/state_management/lens_slice.test.ts +++ b/x-pack/plugins/lens/public/state_management/lens_slice.test.ts @@ -10,6 +10,7 @@ import type { Query } from '@kbn/es-query'; import { switchDatasource, switchAndCleanDatasource, + updateStateFromSuggestion, switchVisualization, setState, updateState, @@ -271,6 +272,28 @@ describe('lensSlice', () => { }); }); + describe('update the state from the suggestion', () => { + it('should switch active datasource and initialize new state', () => { + store.dispatch( + updateStateFromSuggestion({ + newDatasourceId: 'testDatasource2', + visualizationId: 'testVis', + visualizationState: ['col1', 'col2'], + datasourceState: {}, + dataViews: { indexPatterns: {} } as DataViewsState, + }) + ); + expect(store.getState().lens.activeDatasourceId).toEqual('testDatasource2'); + expect(store.getState().lens.datasourceStates.testDatasource2.isLoading).toEqual(false); + expect(store.getState().lens.datasourceStates.testDatasource2.state).toStrictEqual({}); + expect(store.getState().lens.visualization).toStrictEqual({ + activeId: 'testVis', + state: ['col1', 'col2'], + }); + expect(store.getState().lens.dataViews).toEqual({ indexPatterns: {} }); + }); + }); + describe('adding or removing layer', () => { const testDatasource = (datasourceId: string) => { return { diff --git a/x-pack/plugins/lens/public/state_management/lens_slice.ts b/x-pack/plugins/lens/public/state_management/lens_slice.ts index e7829361ca5f9..cb35f16f0e8c5 100644 --- a/x-pack/plugins/lens/public/state_management/lens_slice.ts +++ b/x-pack/plugins/lens/public/state_management/lens_slice.ts @@ -173,6 +173,13 @@ export const switchAndCleanDatasource = createAction<{ visualizationId: string | null; currentIndexPatternId?: string; }>('lens/switchAndCleanDatasource'); +export const updateStateFromSuggestion = createAction<{ + newDatasourceId: string; + visualizationId: string | null; + visualizationState: unknown; + datasourceState: unknown; + dataViews: DataViewsState; +}>('lens/updateStateFromSuggestion'); export const navigateAway = createAction('lens/navigateAway'); export const loadInitial = createAction<{ initialInput?: LensEmbeddableInput; @@ -267,6 +274,7 @@ export const lensActions = { submitSuggestion, switchDatasource, switchAndCleanDatasource, + updateStateFromSuggestion, navigateAway, loadInitial, initEmpty, @@ -848,6 +856,42 @@ export const makeLensReducer = (storeDeps: LensStoreDeps) => { }, }; }, + [updateStateFromSuggestion.type]: ( + state, + { + payload, + }: { + payload: { + newDatasourceId: string; + visualizationId: string; + visualizationState: unknown; + datasourceState: unknown; + dataViews: DataViewsState; + }; + } + ) => { + const visualization = { + activeId: payload.visualizationId, + state: payload.visualizationState, + }; + + const datasourceState = payload.datasourceState; + + return { + ...state, + datasourceStates: { + [payload.newDatasourceId]: { + state: datasourceState, + isLoading: false, + }, + }, + activeDatasourceId: payload.newDatasourceId, + visualization: { + ...visualization, + }, + dataViews: payload.dataViews, + }; + }, [navigateAway.type]: (state) => state, [loadInitial.type]: ( state, diff --git a/x-pack/plugins/ml/common/types/capabilities.ts b/x-pack/plugins/ml/common/types/capabilities.ts index 23d641ffa4d1b..c6bf7e9e1c7e5 100644 --- a/x-pack/plugins/ml/common/types/capabilities.ts +++ b/x-pack/plugins/ml/common/types/capabilities.ts @@ -18,6 +18,12 @@ export const apmUserMlCapabilities = { canGetJobs: false, }; +export const featureMlCapabilities = { + isADEnabled: true, + isDFAEnabled: true, + isNLPEnabled: true, +}; + export const userMlCapabilities = { // Anomaly Detection canGetJobs: false, @@ -80,19 +86,21 @@ export const adminMlCapabilities = { canStartStopTrainedModels: false, }; +export type FeatureMlCapabilities = typeof featureMlCapabilities; export type UserMlCapabilities = typeof userMlCapabilities; export type AdminMlCapabilities = typeof adminMlCapabilities; -export type MlCapabilities = UserMlCapabilities & AdminMlCapabilities; +export type MlCapabilities = FeatureMlCapabilities & UserMlCapabilities & AdminMlCapabilities; export type MlCapabilitiesKey = keyof MlCapabilities; -export const basicLicenseMlCapabilities = [ +export const basicLicenseMlCapabilities: MlCapabilitiesKey[] = [ 'canFindFileStructure', 'canGetFieldInfo', 'canGetMlInfo', -] as Array; +]; export function getDefaultCapabilities(): MlCapabilities { return { + ...featureMlCapabilities, ...userMlCapabilities, ...adminMlCapabilities, }; @@ -101,8 +109,13 @@ export function getDefaultCapabilities(): MlCapabilities { export function getPluginPrivileges() { const apmUserMlCapabilitiesKeys = Object.keys(apmUserMlCapabilities); const userMlCapabilitiesKeys = Object.keys(userMlCapabilities); + const featureMlCapabilitiesKeys = Object.keys(featureMlCapabilities); const adminMlCapabilitiesKeys = Object.keys(adminMlCapabilities); - const allMlCapabilitiesKeys = [...adminMlCapabilitiesKeys, ...userMlCapabilitiesKeys]; + const allMlCapabilitiesKeys = [ + ...featureMlCapabilitiesKeys, + ...adminMlCapabilitiesKeys, + ...userMlCapabilitiesKeys, + ]; const savedObjects = [ 'index-pattern', @@ -143,10 +156,13 @@ export function getPluginPrivileges() { }, user: { ...privilege, - api: ['fileUpload:analyzeFile', ...userMlCapabilitiesKeys.map((k) => `ml:${k}`)], + api: [ + 'fileUpload:analyzeFile', + ...[...featureMlCapabilitiesKeys, ...userMlCapabilitiesKeys].map((k) => `ml:${k}`), + ], catalogue: [PLUGIN_ID], management: { insightsAndAlerting: [] }, - ui: userMlCapabilitiesKeys, + ui: [...featureMlCapabilitiesKeys, ...userMlCapabilitiesKeys], savedObject: { all: [], read: savedObjects, @@ -182,3 +198,50 @@ export interface MlCapabilitiesResponse { } export type ResolveMlCapabilities = (request: KibanaRequest) => Promise; + +interface FeatureCapabilities { + ad: MlCapabilitiesKey[]; + dfa: MlCapabilitiesKey[]; + nlp: MlCapabilitiesKey[]; +} + +export const featureCapabilities: FeatureCapabilities = { + ad: [ + 'canGetJobs', + 'canGetDatafeeds', + 'canGetCalendars', + 'canGetAnnotations', + 'canCreateAnnotation', + 'canDeleteAnnotation', + 'canCreateJob', + 'canDeleteJob', + 'canOpenJob', + 'canCloseJob', + 'canResetJob', + 'canUpdateJob', + 'canForecastJob', + 'canCreateDatafeed', + 'canDeleteDatafeed', + 'canStartStopDatafeed', + 'canUpdateDatafeed', + 'canPreviewDatafeed', + 'canGetFilters', + 'canCreateCalendar', + 'canDeleteCalendar', + 'canCreateFilter', + 'canDeleteFilter', + ], + dfa: [ + 'canGetDataFrameAnalytics', + 'canCreateDataFrameAnalytics', + 'canDeleteDataFrameAnalytics', + 'canStartStopDataFrameAnalytics', + ], + nlp: [ + 'canGetTrainedModels', + 'canTestTrainedModels', + 'canCreateTrainedModels', + 'canDeleteTrainedModels', + 'canStartStopTrainedModels', + ], +}; diff --git a/x-pack/plugins/ml/public/application/app.tsx b/x-pack/plugins/ml/public/application/app.tsx index 1cc165acfbcdb..559ec601da4d7 100644 --- a/x-pack/plugins/ml/public/application/app.tsx +++ b/x-pack/plugins/ml/public/application/app.tsx @@ -9,7 +9,8 @@ import React, { type FC, useMemo } from 'react'; import './_index.scss'; import ReactDOM from 'react-dom'; import { pick } from 'lodash'; -import { AppMountParameters, CoreStart, HttpStart } from '@kbn/core/public'; + +import type { AppMountParameters, CoreStart, HttpStart } from '@kbn/core/public'; import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; import { DatePickerContextProvider } from '@kbn/ml-date-picker'; import { Storage } from '@kbn/kibana-utils-plugin/public'; @@ -33,6 +34,7 @@ import { mlUsageCollectionProvider } from './services/usage_collection'; import { MlRouter } from './routing'; import { mlApiServicesProvider } from './services/ml_api_service'; import { HttpService } from './services/http_service'; +import type { PageDependencies } from './routing/router'; export type MlDependencies = Omit< MlSetupDependencies, @@ -48,12 +50,6 @@ interface AppProps { const localStorage = new Storage(window.localStorage); -// temporary function to hardcode the serverless state -// this will be replaced by the true serverless information from kibana -export function isServerless() { - return false; -} - /** * Provides global services available across the entire ML app. */ @@ -65,7 +61,6 @@ export function getMlGlobalServices(httpStart: HttpStart, usageCollection?: Usag httpService, mlApiServices, mlUsageCollection: mlUsageCollectionProvider(usageCollection), - isServerless, mlCapabilities: new MlCapabilitiesService(mlApiServices), mlLicense: new MlLicense(), }; @@ -78,7 +73,7 @@ export interface MlServicesContext { export type MlGlobalServices = ReturnType; const App: FC = ({ coreStart, deps, appMountParams }) => { - const pageDeps = { + const pageDeps: PageDependencies = { history: appMountParams.history, setHeaderActionMenu: appMountParams.setHeaderActionMenu, setBreadcrumbs: coreStart.chrome!.setBreadcrumbs, 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 84cb353aa580b..6c981351d5f63 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 @@ -6,7 +6,7 @@ */ import React, { createContext, FC, useEffect, useMemo, useState } from 'react'; -import { createHtmlPortalNode, HtmlPortalNode } from 'react-reverse-portal'; +import { createHtmlPortalNode, type HtmlPortalNode } from 'react-reverse-portal'; import { Redirect } from 'react-router-dom'; import { Routes, Route } from '@kbn/shared-ux-router'; import { Subscription } from 'rxjs'; @@ -21,13 +21,14 @@ import { DatePickerWrapper } from '@kbn/ml-date-picker'; import * as routes from '../../routing/routes'; import { MlPageWrapper } from '../../routing/ml_page_wrapper'; import { useMlKibana, useNavigateToPath } from '../../contexts/kibana'; -import { MlRoute, PageDependencies } from '../../routing/router'; +import type { MlRoute, PageDependencies } from '../../routing/router'; import { useActiveRoute } from '../../routing/use_active_route'; import { useDocTitle } from '../../routing/use_doc_title'; import { MlPageHeaderRenderer } from '../page_header/page_header'; import { useSideNavItems } from './side_nav'; +import { usePermissionCheck } from '../../capabilities/check_capabilities'; const ML_APP_SELECTOR = '[data-test-subj="mlApp"]'; @@ -60,6 +61,17 @@ export const MlPage: FC<{ pageDeps: PageDependencies }> = React.memo(({ pageDeps const [isHeaderMounted, setIsHeaderMounted] = useState(false); const [isLoading, setIsLoading] = useState(false); + const [isADEnabled, isDFAEnabled, isNLPEnabled] = usePermissionCheck([ + 'isADEnabled', + 'isDFAEnabled', + 'isNLPEnabled', + ]); + + const navMenuEnabled = useMemo( + () => isADEnabled && isDFAEnabled && isNLPEnabled, + [isADEnabled, isDFAEnabled, isNLPEnabled] + ); + useEffect(() => { const subscriptions = new Subscription(); @@ -68,7 +80,6 @@ export const MlPage: FC<{ pageDeps: PageDependencies }> = React.memo(({ pageDeps setIsLoading(v !== 0); }) ); - return function cleanup() { subscriptions.unsubscribe(); }; @@ -111,6 +122,8 @@ export const MlPage: FC<{ pageDeps: PageDependencies }> = React.memo(({ pageDeps } }, [activeRoute]); + const sideNavItems = useSideNavItems(activeRoute); + return ( = React.memo(({ pageDeps className={'ml-app'} data-test-subj={'mlApp'} restrictWidth={false} - solutionNav={{ - name: i18n.translate('xpack.ml.plugin.title', { - defaultMessage: 'Machine Learning', - }), - icon: 'machineLearningApp', - items: useSideNavItems(activeRoute), - }} + solutionNav={ + navMenuEnabled + ? { + name: i18n.translate('xpack.ml.plugin.title', { + defaultMessage: 'Machine Learning', + }), + icon: 'machineLearningApp', + items: sideNavItems, + } + : undefined + } pageHeader={{ pageTitle: , rightSideItems, diff --git a/x-pack/plugins/ml/public/application/contexts/kibana/use_is_serverless.ts b/x-pack/plugins/ml/public/application/contexts/kibana/use_is_serverless.ts deleted file mode 100644 index 120ae02b8d466..0000000000000 --- a/x-pack/plugins/ml/public/application/contexts/kibana/use_is_serverless.ts +++ /dev/null @@ -1,14 +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 { useMemo } from 'react'; -import { useMlKibana } from './kibana_context'; - -export const useIsServerless = () => { - const isServerless = useMlKibana().services.mlServices.isServerless; - return useMemo(() => isServerless(), [isServerless]); -}; diff --git a/x-pack/plugins/ml/public/application/memory_usage/memory_usage_page.tsx b/x-pack/plugins/ml/public/application/memory_usage/memory_usage_page.tsx index 24e3dbab771b5..9fe3261aec74c 100644 --- a/x-pack/plugins/ml/public/application/memory_usage/memory_usage_page.tsx +++ b/x-pack/plugins/ml/public/application/memory_usage/memory_usage_page.tsx @@ -12,8 +12,8 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { NodesList } from './nodes_overview'; import { MlPageHeader } from '../components/page_header'; import { MemoryPage, JobMemoryTreeMap } from './memory_tree_map'; -import { useIsServerless } from '../contexts/kibana/use_is_serverless'; import { SavedObjectsWarning } from '../components/saved_objects_warning'; +import { usePermissionCheck } from '../capabilities/check_capabilities'; enum TAB { NODES, @@ -21,9 +21,13 @@ enum TAB { } export const MemoryUsagePage: FC = () => { - const serverless = useIsServerless(); const [selectedTab, setSelectedTab] = useState(TAB.NODES); useTimefilter({ timeRangeSelector: false, autoRefreshSelector: true }); + const [isADEnabled, isDFAEnabled, isNLPEnabled] = usePermissionCheck([ + 'isADEnabled', + 'isDFAEnabled', + 'isNLPEnabled', + ]); const refresh = useCallback(() => { mlTimefilterRefresh$.next({ @@ -46,7 +50,7 @@ export const MemoryUsagePage: FC = () => { - {serverless ? ( + {isADEnabled && isDFAEnabled && isNLPEnabled ? ( ) : ( <> diff --git a/x-pack/plugins/ml/public/application/model_management/model_actions.tsx b/x-pack/plugins/ml/public/application/model_management/model_actions.tsx index 1868ae0b8d85b..09ba11f56c747 100644 --- a/x-pack/plugins/ml/public/application/model_management/model_actions.tsx +++ b/x-pack/plugins/ml/public/application/model_management/model_actions.tsx @@ -80,7 +80,7 @@ export function useModelActions({ cluster: ['manage_ingest_pipelines'], }) .then((result) => { - const canManagePipelines = result.cluster.manage_ingest_pipelines; + const canManagePipelines = result.cluster?.manage_ingest_pipelines; if (isMounted) { setCanManageIngestPipelines(canManagePipelines); } diff --git a/x-pack/plugins/ml/public/application/notifications/components/notifications_list.tsx b/x-pack/plugins/ml/public/application/notifications/components/notifications_list.tsx index 7b08bcec0331f..4c04333b71e61 100644 --- a/x-pack/plugins/ml/public/application/notifications/components/notifications_list.tsx +++ b/x-pack/plugins/ml/public/application/notifications/components/notifications_list.tsx @@ -73,10 +73,12 @@ export const NotificationsList: FC = () => { const timeRange = useTimeRangeUpdates(); useMount(function setTimeRangeOnMount() { - timeFilter.setTime({ - from: moment(latestRequestedAt).toISOString(), - to: 'now', - }); + if (latestRequestedAt !== null) { + timeFilter.setTime({ + from: moment(latestRequestedAt).toISOString(), + to: 'now', + }); + } }); const [isLoading, setIsLoading] = useState(true); diff --git a/x-pack/plugins/ml/public/application/overview/components/content.tsx b/x-pack/plugins/ml/public/application/overview/components/content.tsx index 992a64015e7e4..15998b9f58b93 100644 --- a/x-pack/plugins/ml/public/application/overview/components/content.tsx +++ b/x-pack/plugins/ml/public/application/overview/components/content.tsx @@ -13,6 +13,7 @@ import { AnalyticsPanel } from './analytics_panel'; import { AnomalyTimelineService } from '../../services/anomaly_timeline_service'; import { mlResultsServiceProvider } from '../../services/results_service'; import { useMlKibana } from '../../contexts/kibana'; +import { usePermissionCheck } from '../../capabilities/check_capabilities'; interface Props { createAnomalyDetectionJobDisabled: boolean; @@ -32,6 +33,8 @@ export const OverviewContent: FC = ({ }, } = useMlKibana(); + const [isADEnabled, isDFAEnabled] = usePermissionCheck(['isADEnabled', 'isDFAEnabled']); + const timefilter = useTimefilter(); const [anomalyTimelineService, setAnomalyTimelineService] = useState(); @@ -49,12 +52,17 @@ export const OverviewContent: FC = ({ return ( <> - - - + {isADEnabled ? ( + <> + + + + ) : null} + + {isDFAEnabled ? : null} ); }; diff --git a/x-pack/plugins/ml/public/application/overview/overview_page.tsx b/x-pack/plugins/ml/public/application/overview/overview_page.tsx index 6772125bb9532..aad0dc45a3548 100644 --- a/x-pack/plugins/ml/public/application/overview/overview_page.tsx +++ b/x-pack/plugins/ml/public/application/overview/overview_page.tsx @@ -27,7 +27,6 @@ import { useMlKibana, useMlLink } from '../contexts/kibana'; import { NodesList } from '../memory_usage/nodes_overview'; import { MlPageHeader } from '../components/page_header'; import { PageTitle } from '../components/page_title'; -import { useIsServerless } from '../contexts/kibana/use_is_serverless'; import { getMlNodesCount } from '../ml_nodes_check/check_ml_nodes'; export const overviewPanelDefaultState = Object.freeze({ @@ -37,7 +36,6 @@ export const overviewPanelDefaultState = Object.freeze({ }); export const OverviewPage: FC = () => { - const serverless = useIsServerless(); const [canViewMlNodes, canCreateJob] = usePermissionCheck(['canViewMlNodes', 'canCreateJob']); const disableCreateAnomalyDetectionJob = !canCreateJob || !mlNodesAvailable(); @@ -83,7 +81,7 @@ export const OverviewPage: FC = () => { /> - {canViewMlNodes && serverless === false ? ( + {canViewMlNodes ? ( <> { - const { context } = useRouteResolver('full', ['canGetJobs'], { + const { context } = useRouteResolver('full', ['canGetMlInfo'], { getMlNodeCount, loadMlServerInfo, }); diff --git a/x-pack/plugins/ml/public/application/routing/routes/overview.tsx b/x-pack/plugins/ml/public/application/routing/routes/overview.tsx index 3dec773e24a1f..33346b0d0fe72 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/overview.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/overview.tsx @@ -42,7 +42,7 @@ export const overviewRouteFactory = ( }); const PageWrapper: FC = () => { - const { context } = useRouteResolver('full', ['canGetJobs'], { + const { context } = useRouteResolver('full', ['canGetMlInfo'], { getMlNodeCount, loadMlServerInfo, }); diff --git a/x-pack/plugins/ml/public/plugin.ts b/x-pack/plugins/ml/public/plugin.ts index f8362b3a92353..f9f3b6d22b0fc 100644 --- a/x-pack/plugins/ml/public/plugin.ts +++ b/x-pack/plugins/ml/public/plugin.ts @@ -25,7 +25,7 @@ import type { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/pu import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import type { LensPublicStart } from '@kbn/lens-plugin/public'; -import { AppStatus, AppUpdater, DEFAULT_APP_CATEGORIES } from '@kbn/core/public'; +import { AppStatus, type AppUpdater, DEFAULT_APP_CATEGORIES } from '@kbn/core/public'; import type { UiActionsSetup, UiActionsStart } from '@kbn/ui-actions-plugin/public'; import type { LicenseManagementUIPluginSetup } from '@kbn/license-management-plugin/public'; @@ -47,11 +47,12 @@ import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; import type { CasesUiSetup, CasesUiStart } from '@kbn/cases-plugin/public'; import type { SavedSearchPublicPluginStart } from '@kbn/saved-search-plugin/public'; import { registerManagementSection } from './application/management'; -import { MlLocatorDefinition, MlLocator } from './locator'; +import { MlLocatorDefinition, type MlLocator } from './locator'; import { setDependencyCache } from './application/util/dependency_cache'; import { registerFeature } from './register_feature'; import { isFullLicense, isMlEnabled } from '../common/license'; import { PLUGIN_ICON_SOLUTION, PLUGIN_ID } from '../common/constants/app'; +import type { MlCapabilities } from './shared'; export interface MlStartDependencies { data: DataPublicPluginStart; @@ -195,7 +196,7 @@ export class MlPlugin implements Plugin { } if (mlEnabled) { - registerSearchLinks(this.appUpdater$, fullLicense); + registerSearchLinks(this.appUpdater$, fullLicense, capabilities.ml as MlCapabilities); if (fullLicense) { registerEmbeddables(pluginsSetup.embeddable, core); diff --git a/x-pack/plugins/ml/public/register_helper/register_search_links/register_search_links.ts b/x-pack/plugins/ml/public/register_helper/register_search_links/register_search_links.ts index a25d7e24a7274..35c37c56ecdff 100644 --- a/x-pack/plugins/ml/public/register_helper/register_search_links/register_search_links.ts +++ b/x-pack/plugins/ml/public/register_helper/register_search_links/register_search_links.ts @@ -8,12 +8,14 @@ import { i18n } from '@kbn/i18n'; import { BehaviorSubject } from 'rxjs'; -import { AppUpdater } from '@kbn/core/public'; +import type { AppUpdater } from '@kbn/core/public'; import { getDeepLinks } from './search_deep_links'; +import type { MlCapabilities } from '../../shared'; export function registerSearchLinks( appUpdater: BehaviorSubject, - isFullLicense: boolean + isFullLicense: boolean, + mlCapabilities: MlCapabilities ) { appUpdater.next(() => ({ keywords: [ @@ -21,6 +23,6 @@ export function registerSearchLinks( defaultMessage: 'ML', }), ], - deepLinks: getDeepLinks(isFullLicense), + deepLinks: getDeepLinks(isFullLicense, mlCapabilities), })); } diff --git a/x-pack/plugins/ml/public/register_helper/register_search_links/search_deep_links.ts b/x-pack/plugins/ml/public/register_helper/register_search_links/search_deep_links.ts index d892c949b1d52..38e48db3787af 100644 --- a/x-pack/plugins/ml/public/register_helper/register_search_links/search_deep_links.ts +++ b/x-pack/plugins/ml/public/register_helper/register_search_links/search_deep_links.ts @@ -8,203 +8,266 @@ import { i18n } from '@kbn/i18n'; import type { LinkId } from '@kbn/deeplinks-ml'; -import type { AppDeepLink } from '@kbn/core/public'; +import { type AppDeepLink, AppNavLinkStatus } from '@kbn/core/public'; import { ML_PAGES } from '../../../common/constants/locator'; +import type { MlCapabilities } from '../../shared'; -const OVERVIEW_LINK_DEEP_LINK: AppDeepLink = { - id: 'overview', - title: i18n.translate('xpack.ml.deepLink.overview', { - defaultMessage: 'Overview', - }), - path: `/${ML_PAGES.OVERVIEW}`, -}; - -const ANOMALY_DETECTION_DEEP_LINK: AppDeepLink = { - id: 'anomalyDetection', - title: i18n.translate('xpack.ml.deepLink.anomalyDetection', { - defaultMessage: 'Anomaly Detection', - }), - path: `/${ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE}`, - deepLinks: [ - { - id: 'anomalyExplorer', - title: i18n.translate('xpack.ml.deepLink.anomalyExplorer', { - defaultMessage: 'Anomaly explorer', - }), - path: `/${ML_PAGES.ANOMALY_EXPLORER}`, - }, - { - id: 'singleMetricViewer', - title: i18n.translate('xpack.ml.deepLink.singleMetricViewer', { - defaultMessage: 'Single metric viewer', - }), - path: `/${ML_PAGES.SINGLE_METRIC_VIEWER}`, - }, - ], -}; - -const DATA_FRAME_ANALYTICS_DEEP_LINK: AppDeepLink = { - id: 'dataFrameAnalytics', - title: i18n.translate('xpack.ml.deepLink.dataFrameAnalytics', { - defaultMessage: 'Data Frame Analytics', - }), - path: `/${ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE}`, - deepLinks: [ - { - id: 'resultExplorer', - title: i18n.translate('xpack.ml.deepLink.resultExplorer', { - defaultMessage: 'Results explorer', - }), - path: `/${ML_PAGES.DATA_FRAME_ANALYTICS_EXPLORATION}`, - }, - { - id: 'analyticsMap', - title: i18n.translate('xpack.ml.deepLink.analyticsMap', { - defaultMessage: 'Analytics map', - }), - path: `/${ML_PAGES.DATA_FRAME_ANALYTICS_MAP}`, - }, - ], -}; - -const AIOPS_DEEP_LINK: AppDeepLink = { - id: 'aiOps', - title: i18n.translate('xpack.ml.deepLink.aiOps', { - defaultMessage: 'AIOps', - }), - // Default to the index select page for the explain log rate spikes since we don't have an AIops overview page - path: `/${ML_PAGES.AIOPS_EXPLAIN_LOG_RATE_SPIKES_INDEX_SELECT}`, - deepLinks: [ - { - id: 'explainLogRateSpikes', - title: i18n.translate('xpack.ml.deepLink.explainLogRateSpikes', { - defaultMessage: 'Explain Log Rate Spikes', - }), - path: `/${ML_PAGES.AIOPS_EXPLAIN_LOG_RATE_SPIKES_INDEX_SELECT}`, - }, - { - id: 'logPatternAnalysis', - title: i18n.translate('xpack.ml.deepLink.logPatternAnalysis', { - defaultMessage: 'Log Pattern Analysis', - }), - path: `/${ML_PAGES.AIOPS_LOG_CATEGORIZATION_INDEX_SELECT}`, - }, - { - id: 'changePointDetections', - title: i18n.translate('xpack.ml.deepLink.changePointDetection', { - defaultMessage: 'Change Point Detection', - }), - path: `/${ML_PAGES.AIOPS_CHANGE_POINT_DETECTION_INDEX_SELECT}`, - }, - ], -}; - -const MODEL_MANAGEMENT_DEEP_LINK: AppDeepLink = { - id: 'modelManagement', - title: i18n.translate('xpack.ml.deepLink.modelManagement', { - defaultMessage: 'Model Management', - }), - path: `/${ML_PAGES.TRAINED_MODELS_MANAGE}`, - deepLinks: [ - { - id: 'nodesOverview', - title: i18n.translate('xpack.ml.deepLink.trainedModels', { - defaultMessage: 'Trained Models', - }), - path: `/${ML_PAGES.TRAINED_MODELS_MANAGE}`, - }, - { - id: 'nodes', - title: i18n.translate('xpack.ml.deepLink.nodes', { - defaultMessage: 'Nodes', - }), - path: `/${ML_PAGES.NODES}`, - }, - ], -}; - -const MEMORY_USAGE_DEEP_LINK: AppDeepLink = { - id: 'memoryUsage', - title: i18n.translate('xpack.ml.deepLink.memoryUsage', { - defaultMessage: 'Memory Usage', - }), - path: `/${ML_PAGES.MEMORY_USAGE}`, -}; - -const DATA_VISUALIZER_DEEP_LINK: AppDeepLink = { - id: 'dataVisualizer', - title: i18n.translate('xpack.ml.deepLink.dataVisualizer', { - defaultMessage: 'Data Visualizer', - }), - path: `/${ML_PAGES.DATA_VISUALIZER}`, -}; - -const FILE_UPLOAD_DEEP_LINK: AppDeepLink = { - id: 'fileUpload', - title: i18n.translate('xpack.ml.deepLink.fileUpload', { - defaultMessage: 'File Upload', - }), - keywords: ['CSV', 'JSON'], - path: `/${ML_PAGES.DATA_VISUALIZER_FILE}`, -}; - -const INDEX_DATA_VISUALIZER_DEEP_LINK: AppDeepLink = { - id: 'indexDataVisualizer', - title: i18n.translate('xpack.ml.deepLink.indexDataVisualizer', { - defaultMessage: 'Index Data Visualizer', - }), - path: `/${ML_PAGES.DATA_VISUALIZER_INDEX_SELECT}`, -}; - -const SETTINGS_DEEP_LINK: AppDeepLink = { - id: 'settings', - title: i18n.translate('xpack.ml.deepLink.settings', { - defaultMessage: 'Settings', - }), - path: `/${ML_PAGES.SETTINGS}`, - deepLinks: [ - { - id: 'calendarSettings', - title: i18n.translate('xpack.ml.deepLink.calendarSettings', { - defaultMessage: 'Calendars', - }), - path: `/${ML_PAGES.CALENDARS_MANAGE}`, - }, - { - id: 'filterListsSettings', - title: i18n.translate('xpack.ml.deepLink.filterListsSettings', { - defaultMessage: 'Filter Lists', - }), - path: `/${ML_PAGES.SETTINGS}`, // Link to settings page as read only users cannot view filter lists. - }, - ], -}; - -const NOTIFICATIONS_DEEP_LINK: AppDeepLink = { - id: 'notifications', - title: i18n.translate('xpack.ml.deepLink.notifications', { - defaultMessage: 'Notifications', - }), - path: `/${ML_PAGES.NOTIFICATIONS}`, -}; - -export function getDeepLinks(isFullLicense: boolean) { +function getNavStatus( + mlCapabilities: MlCapabilities, + statusIfServerless: boolean +): AppNavLinkStatus | undefined { + if (mlCapabilities.isADEnabled && mlCapabilities.isDFAEnabled && mlCapabilities.isNLPEnabled) { + // if all features are enabled we can assume that we are not running in serverless mode. + // returning default will not add the link to the nav menu, but the link will be registered for searching + return AppNavLinkStatus.default; + } + + return statusIfServerless ? AppNavLinkStatus.visible : AppNavLinkStatus.hidden; +} + +function getOverviewLinkDeepLink(mlCapabilities: MlCapabilities): AppDeepLink { + return { + id: 'overview', + title: i18n.translate('xpack.ml.deepLink.overview', { + defaultMessage: 'Overview', + }), + path: `/${ML_PAGES.OVERVIEW}`, + navLinkStatus: getNavStatus(mlCapabilities, false), + }; +} + +function getAnomalyDetectionDeepLink(mlCapabilities: MlCapabilities): AppDeepLink { + const navLinkStatus = getNavStatus(mlCapabilities, mlCapabilities.isADEnabled); + return { + id: 'anomalyDetection', + title: i18n.translate('xpack.ml.deepLink.anomalyDetection', { + defaultMessage: 'Anomaly Detection', + }), + path: `/${ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE}`, + navLinkStatus, + deepLinks: [ + { + id: 'anomalyExplorer', + title: i18n.translate('xpack.ml.deepLink.anomalyExplorer', { + defaultMessage: 'Anomaly explorer', + }), + path: `/${ML_PAGES.ANOMALY_EXPLORER}`, + navLinkStatus, + }, + { + id: 'singleMetricViewer', + title: i18n.translate('xpack.ml.deepLink.singleMetricViewer', { + defaultMessage: 'Single metric viewer', + }), + path: `/${ML_PAGES.SINGLE_METRIC_VIEWER}`, + navLinkStatus, + }, + ], + }; +} + +function getDataFrameAnalyticsDeepLink(mlCapabilities: MlCapabilities): AppDeepLink { + const navLinkStatus = getNavStatus(mlCapabilities, mlCapabilities.isDFAEnabled); + return { + id: 'dataFrameAnalytics', + title: i18n.translate('xpack.ml.deepLink.dataFrameAnalytics', { + defaultMessage: 'Data Frame Analytics', + }), + path: `/${ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE}`, + navLinkStatus, + deepLinks: [ + { + id: 'resultExplorer', + title: i18n.translate('xpack.ml.deepLink.resultExplorer', { + defaultMessage: 'Results explorer', + }), + path: `/${ML_PAGES.DATA_FRAME_ANALYTICS_EXPLORATION}`, + navLinkStatus, + }, + { + id: 'analyticsMap', + title: i18n.translate('xpack.ml.deepLink.analyticsMap', { + defaultMessage: 'Analytics map', + }), + path: `/${ML_PAGES.DATA_FRAME_ANALYTICS_MAP}`, + navLinkStatus, + }, + ], + }; +} + +function getAiopsDeepLink(mlCapabilities: MlCapabilities): AppDeepLink { + const navLinkStatus = getNavStatus(mlCapabilities, mlCapabilities.canUseAiops); + return { + id: 'aiOps', + title: i18n.translate('xpack.ml.deepLink.aiOps', { + defaultMessage: 'AIOps', + }), + // Default to the index select page for the explain log rate spikes since we don't have an AIops overview page + path: `/${ML_PAGES.AIOPS_EXPLAIN_LOG_RATE_SPIKES_INDEX_SELECT}`, + navLinkStatus, + deepLinks: [ + { + id: 'explainLogRateSpikes', + title: i18n.translate('xpack.ml.deepLink.explainLogRateSpikes', { + defaultMessage: 'Explain Log Rate Spikes', + }), + path: `/${ML_PAGES.AIOPS_EXPLAIN_LOG_RATE_SPIKES_INDEX_SELECT}`, + navLinkStatus, + }, + { + id: 'logPatternAnalysis', + title: i18n.translate('xpack.ml.deepLink.logPatternAnalysis', { + defaultMessage: 'Log Pattern Analysis', + }), + path: `/${ML_PAGES.AIOPS_LOG_CATEGORIZATION_INDEX_SELECT}`, + navLinkStatus, + }, + { + id: 'changePointDetections', + title: i18n.translate('xpack.ml.deepLink.changePointDetection', { + defaultMessage: 'Change Point Detection', + }), + path: `/${ML_PAGES.AIOPS_CHANGE_POINT_DETECTION_INDEX_SELECT}`, + navLinkStatus, + }, + ], + }; +} + +function getModelManagementDeepLink(mlCapabilities: MlCapabilities): AppDeepLink { + const navLinkStatus = getNavStatus(mlCapabilities, mlCapabilities.isNLPEnabled); + return { + id: 'modelManagement', + title: i18n.translate('xpack.ml.deepLink.modelManagement', { + defaultMessage: 'Model Management', + }), + path: `/${ML_PAGES.TRAINED_MODELS_MANAGE}`, + navLinkStatus, + deepLinks: [ + { + id: 'nodesOverview', + title: i18n.translate('xpack.ml.deepLink.trainedModels', { + defaultMessage: 'Trained Models', + }), + path: `/${ML_PAGES.TRAINED_MODELS_MANAGE}`, + navLinkStatus, + }, + { + id: 'nodes', + title: i18n.translate('xpack.ml.deepLink.nodes', { + defaultMessage: 'Nodes', + }), + path: `/${ML_PAGES.NODES}`, + navLinkStatus: getNavStatus(mlCapabilities, false), + }, + ], + }; +} + +function getMemoryUsageDeepLink(mlCapabilities: MlCapabilities): AppDeepLink { + return { + id: 'memoryUsage', + title: i18n.translate('xpack.ml.deepLink.memoryUsage', { + defaultMessage: 'Memory Usage', + }), + path: `/${ML_PAGES.MEMORY_USAGE}`, + navLinkStatus: getNavStatus(mlCapabilities, false), + }; +} + +function getDataVisualizerDeepLink(mlCapabilities: MlCapabilities): AppDeepLink { + return { + id: 'dataVisualizer', + title: i18n.translate('xpack.ml.deepLink.dataVisualizer', { + defaultMessage: 'Data Visualizer', + }), + path: `/${ML_PAGES.DATA_VISUALIZER}`, + navLinkStatus: getNavStatus(mlCapabilities, false), + }; +} + +function getFileUploadDeepLink(mlCapabilities: MlCapabilities): AppDeepLink { + return { + id: 'fileUpload', + title: i18n.translate('xpack.ml.deepLink.fileUpload', { + defaultMessage: 'File Upload', + }), + keywords: ['CSV', 'JSON'], + path: `/${ML_PAGES.DATA_VISUALIZER_FILE}`, + navLinkStatus: getNavStatus(mlCapabilities, false), + }; +} + +function getIndexDataVisualizerDeepLink(mlCapabilities: MlCapabilities): AppDeepLink { + return { + id: 'indexDataVisualizer', + title: i18n.translate('xpack.ml.deepLink.indexDataVisualizer', { + defaultMessage: 'Index Data Visualizer', + }), + path: `/${ML_PAGES.DATA_VISUALIZER_INDEX_SELECT}`, + navLinkStatus: getNavStatus(mlCapabilities, false), + }; +} + +function getSettingsDeepLink(mlCapabilities: MlCapabilities): AppDeepLink { + const navLinkStatus = getNavStatus(mlCapabilities, mlCapabilities.isADEnabled); + return { + id: 'settings', + title: i18n.translate('xpack.ml.deepLink.settings', { + defaultMessage: 'Settings', + }), + path: `/${ML_PAGES.SETTINGS}`, + navLinkStatus, + deepLinks: [ + { + id: 'calendarSettings', + title: i18n.translate('xpack.ml.deepLink.calendarSettings', { + defaultMessage: 'Calendars', + }), + path: `/${ML_PAGES.CALENDARS_MANAGE}`, + navLinkStatus, + }, + { + id: 'filterListsSettings', + title: i18n.translate('xpack.ml.deepLink.filterListsSettings', { + defaultMessage: 'Filter Lists', + }), + path: `/${ML_PAGES.SETTINGS}`, // Link to settings page as read only users cannot view filter lists. + navLinkStatus, + }, + ], + }; +} + +function getNotificationsDeepLink(mlCapabilities: MlCapabilities): AppDeepLink { + return { + id: 'notifications', + title: i18n.translate('xpack.ml.deepLink.notifications', { + defaultMessage: 'Notifications', + }), + path: `/${ML_PAGES.NOTIFICATIONS}`, + navLinkStatus: getNavStatus(mlCapabilities, true), + }; +} + +export function getDeepLinks(isFullLicense: boolean, mlCapabilities: MlCapabilities) { const deepLinks: Array> = [ - DATA_VISUALIZER_DEEP_LINK, - FILE_UPLOAD_DEEP_LINK, - INDEX_DATA_VISUALIZER_DEEP_LINK, + getDataVisualizerDeepLink(mlCapabilities), + getFileUploadDeepLink(mlCapabilities), + getIndexDataVisualizerDeepLink(mlCapabilities), ]; if (isFullLicense === true) { deepLinks.push( - OVERVIEW_LINK_DEEP_LINK, - ANOMALY_DETECTION_DEEP_LINK, - DATA_FRAME_ANALYTICS_DEEP_LINK, - MODEL_MANAGEMENT_DEEP_LINK, - MEMORY_USAGE_DEEP_LINK, - SETTINGS_DEEP_LINK, - AIOPS_DEEP_LINK, - NOTIFICATIONS_DEEP_LINK + getOverviewLinkDeepLink(mlCapabilities), + getAnomalyDetectionDeepLink(mlCapabilities), + getDataFrameAnalyticsDeepLink(mlCapabilities), + getModelManagementDeepLink(mlCapabilities), + getMemoryUsageDeepLink(mlCapabilities), + getSettingsDeepLink(mlCapabilities), + getAiopsDeepLink(mlCapabilities), + getNotificationsDeepLink(mlCapabilities) ); } diff --git a/x-pack/plugins/ml/server/lib/capabilities/capabilities_switcher.ts b/x-pack/plugins/ml/server/lib/capabilities/capabilities_switcher.ts index bc90437e8fcc6..2be5bd877552a 100644 --- a/x-pack/plugins/ml/server/lib/capabilities/capabilities_switcher.ts +++ b/x-pack/plugins/ml/server/lib/capabilities/capabilities_switcher.ts @@ -7,20 +7,30 @@ import { cloneDeep } from 'lodash'; import { firstValueFrom, Observable } from 'rxjs'; -import { CapabilitiesSwitcher, CoreSetup, Logger } from '@kbn/core/server'; -import { ILicense } from '@kbn/licensing-plugin/common/types'; +import type { CapabilitiesSwitcher, CoreSetup, Logger } from '@kbn/core/server'; +import type { ILicense } from '@kbn/licensing-plugin/common/types'; import { isFullLicense, isMinimumLicense, isMlEnabled } from '../../../common/license'; -import { MlCapabilities, basicLicenseMlCapabilities } from '../../../common/types/capabilities'; +import { + type MlCapabilities, + basicLicenseMlCapabilities, + featureCapabilities, +} from '../../../common/types/capabilities'; +import type { MlFeatures } from '../../types'; export const setupCapabilitiesSwitcher = ( coreSetup: CoreSetup, license$: Observable, + enabledFeatures: MlFeatures, logger: Logger ) => { - coreSetup.capabilities.registerSwitcher(getSwitcher(license$, logger)); + coreSetup.capabilities.registerSwitcher(getSwitcher(license$, logger, enabledFeatures)); }; -function getSwitcher(license$: Observable, logger: Logger): CapabilitiesSwitcher { +function getSwitcher( + license$: Observable, + logger: Logger, + enabledFeatures: MlFeatures +): CapabilitiesSwitcher { return async (request, capabilities) => { const isAnonymousRequest = !request.route.options.authRequired; if (isAnonymousRequest) { @@ -31,15 +41,15 @@ function getSwitcher(license$: Observable, logger: Logger): Capabiliti const license = await firstValueFrom(license$); const mlEnabled = isMlEnabled(license); + const originalCapabilities = capabilities.ml as MlCapabilities; + const mlCaps = cloneDeep(originalCapabilities); + // full license, leave capabilities as they were if (mlEnabled && isFullLicense(license)) { - return {}; + return { ml: applyEnabledFeatures(mlCaps, enabledFeatures) }; } - const originalCapabilities = capabilities.ml as MlCapabilities; - const mlCaps = cloneDeep(originalCapabilities); - - // not full licence, switch off all capabilities + // not full license, switch off all capabilities Object.keys(mlCaps).forEach((k) => { mlCaps[k as keyof MlCapabilities] = false; }); @@ -49,10 +59,31 @@ function getSwitcher(license$: Observable, logger: Logger): Capabiliti basicLicenseMlCapabilities.forEach((c) => (mlCaps[c] = originalCapabilities[c])); } - return { ml: mlCaps }; + return { ml: applyEnabledFeatures(mlCaps, enabledFeatures) }; } catch (e) { logger.debug(`Error updating capabilities for ML based on licensing: ${e}`); return {}; } }; } + +function applyEnabledFeatures(mlCaps: MlCapabilities, enabledFeatures: MlFeatures) { + mlCaps.isADEnabled = enabledFeatures.ad; + mlCaps.isDFAEnabled = enabledFeatures.dfa; + mlCaps.isNLPEnabled = enabledFeatures.nlp; + + mlCaps.canViewMlNodes = + mlCaps.canViewMlNodes && mlCaps.isADEnabled && mlCaps.isDFAEnabled && mlCaps.isNLPEnabled; + + if (enabledFeatures.ad === false) { + featureCapabilities.ad.forEach((c) => (mlCaps[c] = false)); + } + if (enabledFeatures.dfa === false) { + featureCapabilities.dfa.forEach((c) => (mlCaps[c] = false)); + } + if (enabledFeatures.nlp === false) { + featureCapabilities.nlp.forEach((c) => (mlCaps[c] = false)); + } + + return mlCaps; +} diff --git a/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts b/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts index 93e22c7e099ce..b66af28faff26 100644 --- a/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts +++ b/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts @@ -47,7 +47,7 @@ describe('check_capabilities', () => { ); const { capabilities } = await getCapabilities(); const count = Object.keys(capabilities).length; - expect(count).toBe(39); + expect(count).toBe(42); }); }); @@ -105,6 +105,10 @@ describe('check_capabilities', () => { expect(capabilities.canCreateTrainedModels).toBe(false); expect(capabilities.canDeleteTrainedModels).toBe(false); expect(capabilities.canStartStopTrainedModels).toBe(false); + + expect(capabilities.isADEnabled).toBe(true); + expect(capabilities.isDFAEnabled).toBe(true); + expect(capabilities.isNLPEnabled).toBe(true); }); test('full capabilities', async () => { @@ -160,6 +164,10 @@ describe('check_capabilities', () => { expect(capabilities.canCreateTrainedModels).toBe(true); expect(capabilities.canDeleteTrainedModels).toBe(true); expect(capabilities.canStartStopTrainedModels).toBe(true); + + expect(capabilities.isADEnabled).toBe(true); + expect(capabilities.isDFAEnabled).toBe(true); + expect(capabilities.isNLPEnabled).toBe(true); }); test('upgrade in progress with full capabilities', async () => { @@ -215,6 +223,10 @@ describe('check_capabilities', () => { expect(capabilities.canCreateTrainedModels).toBe(false); expect(capabilities.canDeleteTrainedModels).toBe(false); expect(capabilities.canStartStopTrainedModels).toBe(false); + + expect(capabilities.isADEnabled).toBe(true); + expect(capabilities.isDFAEnabled).toBe(true); + expect(capabilities.isNLPEnabled).toBe(true); }); test('upgrade in progress with partial capabilities', async () => { @@ -270,6 +282,10 @@ describe('check_capabilities', () => { expect(capabilities.canCreateTrainedModels).toBe(false); expect(capabilities.canDeleteTrainedModels).toBe(false); expect(capabilities.canStartStopTrainedModels).toBe(false); + + expect(capabilities.isADEnabled).toBe(true); + expect(capabilities.isDFAEnabled).toBe(true); + expect(capabilities.isNLPEnabled).toBe(true); }); test('full capabilities, ml disabled in space', async () => { @@ -325,6 +341,10 @@ describe('check_capabilities', () => { expect(capabilities.canCreateTrainedModels).toBe(false); expect(capabilities.canDeleteTrainedModels).toBe(false); expect(capabilities.canStartStopTrainedModels).toBe(false); + + expect(capabilities.isADEnabled).toBe(true); + expect(capabilities.isDFAEnabled).toBe(true); + expect(capabilities.isNLPEnabled).toBe(true); }); }); @@ -381,5 +401,9 @@ describe('check_capabilities', () => { expect(capabilities.canCreateTrainedModels).toBe(false); expect(capabilities.canDeleteTrainedModels).toBe(false); expect(capabilities.canStartStopTrainedModels).toBe(false); + + expect(capabilities.isADEnabled).toBe(true); + expect(capabilities.isDFAEnabled).toBe(true); + expect(capabilities.isNLPEnabled).toBe(true); }); }); diff --git a/x-pack/plugins/ml/server/models/notifications_service/notifications_service_provider.ts b/x-pack/plugins/ml/server/models/notifications_service/notifications_service_provider.ts index 57f117561463c..4ee191693e451 100644 --- a/x-pack/plugins/ml/server/models/notifications_service/notifications_service_provider.ts +++ b/x-pack/plugins/ml/server/models/notifications_service/notifications_service_provider.ts @@ -19,13 +19,20 @@ import type { NotificationsCountResponse, NotificationsSearchResponse, } from '../../../common/types/notifications'; +import type { MlFeatures } from '../../types'; const MAX_NOTIFICATIONS_SIZE = 10000; +interface EntityIdsPerType { + type: 'anomaly_detector' | 'data_frame_analytics' | 'inference' | 'system'; + ids?: Array; +} + export class NotificationsService { constructor( private readonly scopedClusterClient: IScopedClusterClient, - private readonly mlSavedObjectService: MLSavedObjectService + private readonly mlSavedObjectService: MLSavedObjectService, + private readonly enabledFeatures: MlFeatures ) {} private getDefaultCountResponse() { @@ -42,17 +49,23 @@ export class NotificationsService { */ private async _getEntityIdsPerType() { const [adJobIds, dfaJobIds, modelIds] = await Promise.all([ - this.mlSavedObjectService.getAnomalyDetectionJobIds(), - this.mlSavedObjectService.getDataFrameAnalyticsJobIds(), - this.mlSavedObjectService.getTrainedModelsIds(), + this.enabledFeatures.ad ? this.mlSavedObjectService.getAnomalyDetectionJobIds() : [], + this.enabledFeatures.dfa ? this.mlSavedObjectService.getDataFrameAnalyticsJobIds() : [], + this.enabledFeatures.nlp ? this.mlSavedObjectService.getTrainedModelsIds() : [], ]); + const idsPerType: EntityIdsPerType[] = [{ type: 'system' }]; + + if (this.enabledFeatures.ad) { + idsPerType.push({ type: 'anomaly_detector', ids: adJobIds }); + } + if (this.enabledFeatures.dfa) { + idsPerType.push({ type: 'data_frame_analytics', ids: dfaJobIds }); + } + if (this.enabledFeatures.ad) { + idsPerType.push({ type: 'inference', ids: modelIds as string[] }); + } - return [ - { type: 'anomaly_detector', ids: adJobIds }, - { type: 'data_frame_analytics', ids: dfaJobIds }, - { type: 'inference', ids: modelIds }, - { type: 'system' }, - ].filter((v) => v.ids === undefined || v.ids.length > 0); + return idsPerType.filter((v) => v.ids === undefined || v.ids.length > 0); } /** diff --git a/x-pack/plugins/ml/server/plugin.ts b/x-pack/plugins/ml/server/plugin.ts index db150f7c42f32..bb6fc5460761b 100644 --- a/x-pack/plugins/ml/server/plugin.ts +++ b/x-pack/plugins/ml/server/plugin.ts @@ -26,7 +26,7 @@ import { FieldFormatsStart } from '@kbn/field-formats-plugin/server'; import type { HomeServerPluginSetup } from '@kbn/home-plugin/server'; import { jsonSchemaRoutes } from './routes/json_schema'; import { notificationsRoutes } from './routes/notifications'; -import type { PluginsSetup, PluginsStart, RouteInitialization } from './types'; +import type { MlFeatures, PluginsSetup, PluginsStart, RouteInitialization } from './types'; import { PLUGIN_ID } from '../common/constants/app'; import type { MlCapabilities } from '../common/types/capabilities'; @@ -73,7 +73,13 @@ import { CASE_ATTACHMENT_TYPE_ID_ANOMALY_EXPLORER_CHARTS, } from '../common/constants/cases'; -export type MlPluginSetup = SharedServices; +type SetFeaturesEnabled = (features: MlFeatures) => void; + +interface MlSetup { + setFeaturesEnabled: SetFeaturesEnabled; +} + +export type MlPluginSetup = SharedServices & MlSetup; export type MlPluginStart = void; export class MlServerPlugin @@ -93,6 +99,11 @@ export class MlServerPlugin private isMlReady: Promise; private setMlReady: () => void = () => {}; private savedObjectsSyncService: SavedObjectsSyncService; + private enabledFeatures: MlFeatures = { + ad: true, + dfa: true, + nlp: true, + }; constructor(ctx: PluginInitializerContext) { this.log = ctx.logger.get(); @@ -149,7 +160,12 @@ export class MlServerPlugin registerKibanaSettings(coreSetup); // initialize capabilities switcher to add license filter to ml capabilities - setupCapabilitiesSwitcher(coreSetup, plugins.licensing.license$, this.log); + setupCapabilitiesSwitcher( + coreSetup, + plugins.licensing.license$, + this.enabledFeatures, + this.log + ); setupSavedObjects(coreSetup.savedObjects); this.savedObjectsSyncService.registerSyncTask( plugins.taskManager, @@ -208,6 +224,7 @@ export class MlServerPlugin coreSetup.getStartServices ), mlLicense: this.mlLicense, + enabledFeatures: this.enabledFeatures, }; annotationRoutes(routeInit, plugins.security); @@ -269,7 +286,19 @@ export class MlServerPlugin }); } - return sharedServicesProviders; + const setFeaturesEnabled = (features: MlFeatures) => { + if (features.ad !== undefined) { + this.enabledFeatures.ad = features.ad; + } + if (features.dfa !== undefined) { + this.enabledFeatures.dfa = features.dfa; + } + if (features.nlp !== undefined) { + this.enabledFeatures.nlp = features.nlp; + } + }; + + return { ...sharedServicesProviders, setFeaturesEnabled }; } public start(coreStart: CoreStart, plugins: PluginsStart): MlPluginStart { diff --git a/x-pack/plugins/ml/server/routes/notifications.ts b/x-pack/plugins/ml/server/routes/notifications.ts index 8e67136e83ddd..f9c64543dd042 100644 --- a/x-pack/plugins/ml/server/routes/notifications.ts +++ b/x-pack/plugins/ml/server/routes/notifications.ts @@ -12,9 +12,9 @@ import { getNotificationsQuerySchema, } from './schemas/notifications_schema'; import { wrapError } from '../client/error_wrapper'; -import { RouteInitialization } from '../types'; +import type { RouteInitialization } from '../types'; -export function notificationsRoutes({ router, routeGuard }: RouteInitialization) { +export function notificationsRoutes({ router, routeGuard, enabledFeatures }: RouteInitialization) { /** * @apiGroup Notifications * @@ -46,7 +46,11 @@ export function notificationsRoutes({ router, routeGuard }: RouteInitialization) routeGuard.fullLicenseAPIGuard( async ({ client, request, response, mlSavedObjectService }) => { try { - const notificationsService = new NotificationsService(client, mlSavedObjectService); + const notificationsService = new NotificationsService( + client, + mlSavedObjectService, + enabledFeatures + ); const results = await notificationsService.searchMessages(request.query); @@ -91,7 +95,11 @@ export function notificationsRoutes({ router, routeGuard }: RouteInitialization) routeGuard.fullLicenseAPIGuard( async ({ client, mlSavedObjectService, request, response }) => { try { - const notificationsService = new NotificationsService(client, mlSavedObjectService); + const notificationsService = new NotificationsService( + client, + mlSavedObjectService, + enabledFeatures + ); const results = await notificationsService.countMessages(request.query); diff --git a/x-pack/plugins/ml/server/types.ts b/x-pack/plugins/ml/server/types.ts index d054a2289be50..4bca1336ae0b9 100644 --- a/x-pack/plugins/ml/server/types.ts +++ b/x-pack/plugins/ml/server/types.ts @@ -80,4 +80,7 @@ export interface RouteInitialization { router: IRouter; mlLicense: MlLicense; routeGuard: RouteGuard; + enabledFeatures: MlFeatures; } + +export type MlFeatures = Record<'ad' | 'dfa' | 'nlp', boolean>; diff --git a/x-pack/plugins/observability/common/threshold_rule/types.ts b/x-pack/plugins/observability/common/threshold_rule/types.ts index 0762fef113ead..9cf5f48fae1d0 100644 --- a/x-pack/plugins/observability/common/threshold_rule/types.ts +++ b/x-pack/plugins/observability/common/threshold_rule/types.ts @@ -8,9 +8,9 @@ import * as rt from 'io-ts'; import { ML_ANOMALY_THRESHOLD } from '@kbn/ml-anomaly-utils/anomaly_threshold'; import { values } from 'lodash'; +import { SerializedSearchSourceFields } from '@kbn/data-plugin/common'; import { Color } from './color_palette'; import { metricsExplorerMetricRT } from './metrics_explorer'; - import { TimeUnitChar } from '../utils/formatters/duration'; import { SNAPSHOT_CUSTOM_AGGREGATIONS } from './constants'; @@ -201,13 +201,14 @@ export interface MetricAnomalyParams { // Types for the executor -export interface MetricThresholdParams { +export interface ThresholdParams { criteria: MetricExpressionParams[]; filterQuery?: string; filterQueryText?: string; sourceId?: string; alertOnNoData?: boolean; alertOnGroupDisappear?: boolean; + searchConfiguration: SerializedSearchSourceFields; } interface BaseMetricExpressionParams { diff --git a/x-pack/plugins/observability/public/components/threshold/components/alert_flyout.tsx b/x-pack/plugins/observability/public/components/threshold/components/alert_flyout.tsx index cc84ccd5081bd..e0d24d58eb0db 100644 --- a/x-pack/plugins/observability/public/components/threshold/components/alert_flyout.tsx +++ b/x-pack/plugins/observability/public/components/threshold/components/alert_flyout.tsx @@ -28,7 +28,7 @@ export function AlertFlyout(props: Props) { () => triggersActionsUI && triggersActionsUI.getAddRuleFlyout({ - consumer: 'infrastructure', + consumer: 'alerts', onClose: onCloseFlyout, canChangeTrigger: false, ruleTypeId: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, diff --git a/x-pack/plugins/observability/server/lib/rules/threshold/threshold_executor.ts b/x-pack/plugins/observability/server/lib/rules/threshold/threshold_executor.ts index f7387a5764ce2..6fd08c0a15b8e 100644 --- a/x-pack/plugins/observability/server/lib/rules/threshold/threshold_executor.ts +++ b/x-pack/plugins/observability/server/lib/rules/threshold/threshold_executor.ts @@ -71,8 +71,8 @@ export type MetricThresholdAlertContext = { value?: Record | null; }; -export const FIRED_ACTIONS_ID = 'metrics.threshold.fired'; -export const NO_DATA_ACTIONS_ID = 'metrics.threshold.nodata'; +export const FIRED_ACTIONS_ID = 'threshold.fired'; +export const NO_DATA_ACTIONS_ID = 'threshold.nodata'; type MetricThresholdActionGroup = | typeof FIRED_ACTIONS_ID @@ -408,14 +408,14 @@ export const createMetricThresholdExecutor = ({ }; export const FIRED_ACTIONS = { - id: 'metrics.threshold.fired', + id: 'threshold.fired', name: i18n.translate('xpack.observability.threshold.rule.alerting.threshold.fired', { defaultMessage: 'Alert', }), }; export const NO_DATA_ACTIONS = { - id: 'metrics.threshold.nodata', + id: 'threshold.nodata', name: i18n.translate('xpack.observability.threshold.rule.alerting.threshold.nodata', { defaultMessage: 'No Data', }), diff --git a/x-pack/plugins/osquery/common/constants.ts b/x-pack/plugins/osquery/common/constants.ts index 1c0498dc4217a..fe3454090277a 100644 --- a/x-pack/plugins/osquery/common/constants.ts +++ b/x-pack/plugins/osquery/common/constants.ts @@ -20,3 +20,12 @@ export const ACTION_RESPONSES_INDEX = `.logs-${OSQUERY_INTEGRATION_NAME}.action. export const DEFAULT_PLATFORM = 'linux,windows,darwin'; export const CASE_ATTACHMENT_TYPE_ID = 'osquery'; + +export const API_VERSIONS = { + public: { + v1: '2023-10-31', + }, + internal: { + v1: '1', + }, +}; diff --git a/x-pack/plugins/osquery/cypress/e2e/all/packs.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/packs.cy.ts index 933e80435960b..6819186c5bbae 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/packs.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/packs.cy.ts @@ -7,6 +7,8 @@ import { recurse } from 'cypress-recurse'; import { find } from 'lodash'; +import type { PackagePolicy } from '@kbn/fleet-plugin/common'; +import { API_VERSIONS } from '../../../common/constants'; import { FLEET_AGENT_POLICIES, navigateTo } from '../../tasks/navigation'; import { checkActionItemsInResults, @@ -46,6 +48,7 @@ import { loadPack, cleanupAgentPolicy, } from '../../tasks/api_fixtures'; +import { request } from '../../tasks/common'; describe('ALL - Packs', () => { let savedQueryId: string; @@ -225,12 +228,17 @@ describe('ALL - Packs', () => { query: 'select * from uptime;', }, }; - cy.request('/internal/osquery/fleet_wrapper/package_policies').then((response) => { + request<{ items: PackagePolicy[] }>({ + url: '/internal/osquery/fleet_wrapper/package_policies', + headers: { + 'Elastic-Api-Version': API_VERSIONS.internal.v1, + }, + }).then((response) => { const item = response.body.items.find( - (policy: { policy_id: string }) => policy.policy_id === 'fleet-server-policy' + (policy: PackagePolicy) => policy.policy_id === 'fleet-server-policy' ); - expect(item.inputs[0].config.osquery.value.packs[packName].queries).to.deep.equal( + expect(item?.inputs[0].config?.osquery.value.packs[packName].queries).to.deep.equal( queries ); }); @@ -829,10 +837,15 @@ describe('ALL - Packs', () => { addIntegration(agentPolicy); cy.contains('Add Elastic Agent later').click(); cy.contains('osquery_manager-'); - cy.request('/internal/osquery/fleet_wrapper/package_policies').then((response) => { + request<{ items: PackagePolicy[] }>({ + url: '/internal/osquery/fleet_wrapper/package_policies', + headers: { + 'Elastic-Api-Version': API_VERSIONS.internal.v1, + }, + }).then((response) => { const item = find(response.body.items, ['policy_id', agentPolicyId]); - expect(item.inputs[0].config.osquery.value.packs[globalPack]).to.deep.equal({ + expect(item?.inputs[0].config?.osquery.value.packs[globalPack]).to.deep.equal({ shard: 100, queries: {}, }); @@ -879,12 +892,17 @@ describe('ALL - Packs', () => { cy.contains(`Successfully created "${shardPack}" pack`); closeToastIfVisible(); - cy.request('/internal/osquery/fleet_wrapper/package_policies').then((response) => { + request<{ items: PackagePolicy[] }>({ + url: '/internal/osquery/fleet_wrapper/package_policies', + headers: { + 'Elastic-Api-Version': API_VERSIONS.internal.v1, + }, + }).then((response) => { const shardPolicy = response.body.items.find( - (policy: { policy_id: string }) => policy.policy_id === 'fleet-server-policy' + (policy: PackagePolicy) => policy.policy_id === 'fleet-server-policy' ); - expect(shardPolicy.inputs[0].config.osquery.value.packs[shardPack]).to.deep.equal({ + expect(shardPolicy?.inputs[0].config?.osquery.value.packs[shardPack]).to.deep.equal({ shard: 15, queries: {}, }); diff --git a/x-pack/plugins/osquery/cypress/tasks/api_fixtures.ts b/x-pack/plugins/osquery/cypress/tasks/api_fixtures.ts index 2a19019ea3ffb..a87869d693348 100644 --- a/x-pack/plugins/osquery/cypress/tasks/api_fixtures.ts +++ b/x-pack/plugins/osquery/cypress/tasks/api_fixtures.ts @@ -11,6 +11,7 @@ import type { } from '@kbn/security-solution-plugin/common/detection_engine/rule_schema'; import type { AgentPolicy } from '@kbn/fleet-plugin/common'; import type { Case } from '@kbn/cases-plugin/common'; +import { API_VERSIONS } from '../../common/constants'; import type { SavedQuerySOFormData } from '../../public/saved_queries/form/use_saved_query_form'; import type { LiveQueryDetailsItem } from '../../public/actions/use_live_query_details'; import type { PackSavedObject, PackItem } from '../../public/packs/types'; @@ -72,11 +73,20 @@ export const loadSavedQuery = (payload: SavedQuerySOFormData = savedQueryFixture ...payload, id: payload.id ?? generateRandomStringName(1)[0], }, + headers: { + 'Elastic-Api-Version': API_VERSIONS.public.v1, + }, url: '/api/osquery/saved_queries', }).then((response) => response.body.data); export const cleanupSavedQuery = (id: string) => { - request({ method: 'DELETE', url: `/api/osquery/saved_queries/${id}` }); + request({ + method: 'DELETE', + url: `/api/osquery/saved_queries/${id}`, + headers: { + 'Elastic-Api-Version': API_VERSIONS.public.v1, + }, + }); }; export const loadPack = (payload: Partial = {}, space = 'default') => @@ -89,11 +99,20 @@ export const loadPack = (payload: Partial = {}, space = 'default') => queries: payload.queries ?? {}, enabled: payload.enabled || true, }, + headers: { + 'Elastic-Api-Version': API_VERSIONS.public.v1, + }, url: `/s/${space}/api/osquery/packs`, }).then((response) => response.body.data); export const cleanupPack = (id: string, space = 'default') => { - request({ method: 'DELETE', url: `/s/${space}/api/osquery/packs/${id}` }); + request({ + method: 'DELETE', + url: `/s/${space}/api/osquery/packs/${id}`, + headers: { + 'Elastic-Api-Version': API_VERSIONS.public.v1, + }, + }); }; export const loadLiveQuery = ( @@ -108,6 +127,9 @@ export const loadLiveQuery = ( method: 'POST', body: payload, url: `/api/osquery/live_queries`, + headers: { + 'Elastic-Api-Version': API_VERSIONS.public.v1, + }, }).then((response) => response.body.data); export const loadRule = (includeResponseActions = false) => @@ -177,7 +199,13 @@ export const loadRule = (includeResponseActions = false) => }).then((response) => response.body); export const cleanupRule = (id: string) => { - request({ method: 'DELETE', url: `/api/detection_engine/rules?id=${id}` }); + request({ + method: 'DELETE', + url: `/api/detection_engine/rules?id=${id}`, + headers: { + 'Elastic-Api-Version': API_VERSIONS.public.v1, + }, + }); }; export const loadCase = (owner: string) => @@ -197,7 +225,11 @@ export const loadCase = (owner: string) => }).then((response) => response.body); export const cleanupCase = (id: string) => { - request({ method: 'DELETE', url: '/api/cases', qs: { ids: JSON.stringify([id]) } }); + request({ + method: 'DELETE', + url: '/api/cases', + qs: { ids: JSON.stringify([id]) }, + }); }; export const loadSpace = () => { diff --git a/x-pack/plugins/osquery/cypress/tasks/common.ts b/x-pack/plugins/osquery/cypress/tasks/common.ts index c56378642d235..5704796c10b0b 100644 --- a/x-pack/plugins/osquery/cypress/tasks/common.ts +++ b/x-pack/plugins/osquery/cypress/tasks/common.ts @@ -17,6 +17,6 @@ export const request = ( ): Cypress.Chainable> => cy.request({ auth: API_AUTH, - headers: API_HEADERS, ...options, + headers: { ...API_HEADERS, ...options.headers }, }); diff --git a/x-pack/plugins/osquery/cypress/tasks/packs.ts b/x-pack/plugins/osquery/cypress/tasks/packs.ts index 43a007394a485..f8bd23f2c7b10 100644 --- a/x-pack/plugins/osquery/cypress/tasks/packs.ts +++ b/x-pack/plugins/osquery/cypress/tasks/packs.ts @@ -6,6 +6,7 @@ */ import { some } from 'lodash'; +import { API_VERSIONS } from '../../common/constants'; import type { UsePacksResponse } from '../../public/packs/use_packs'; import { request } from './common'; import { closeModalIfVisible, closeToastIfVisible } from './integrations'; @@ -45,6 +46,9 @@ export const cleanupAllPrebuiltPacks = () => { request({ method: 'GET', url: '/api/osquery/packs', + headers: { + 'Elastic-Api-Version': API_VERSIONS.public.v1, + }, }).then((response) => { const prebuiltPacks = response.body.data?.filter((pack) => some(pack.references, { type: 'osquery-pack-asset' }) 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 83508def6a483..f55b3d8ad0eff 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 @@ -6,6 +6,7 @@ */ import { useQuery } from '@tanstack/react-query'; +import { API_VERSIONS } from '../../common/constants'; import { useKibana } from '../common/lib/kibana'; export const useActionResultsPrivileges = () => { @@ -13,7 +14,7 @@ export const useActionResultsPrivileges = () => { return useQuery( ['actionResultsPrivileges'], - () => http.get('/internal/osquery/privileges_check'), + () => http.get('/internal/osquery/privileges_check', { version: API_VERSIONS.internal.v1 }), { keepPreviousData: true, } 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 2f540a3eaf09c..ec124b552b2aa 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 @@ -8,6 +8,7 @@ import { useQuery } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; +import { API_VERSIONS } from '../../common/constants'; import { createFilter } from '../common/helpers'; import { useKibana } from '../common/lib/kibana'; import type { ActionEdges, ActionsStrategyResponse } from '../../common/search_strategy'; @@ -50,6 +51,7 @@ export const useAllLiveQueries = ({ http.get<{ data: Omit & { items: ActionEdges } }>( '/api/osquery/live_queries', { + version: API_VERSIONS.public.v1, query: { filterQuery: createFilter(filterQuery), page: activePage, 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 2a3f070ae1992..bea2a0bc6b6fb 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 @@ -10,6 +10,7 @@ import { useQuery } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; import { filter } from 'lodash'; import type { ECSMapping } from '@kbn/osquery-io-ts-types'; +import { API_VERSIONS } from '../../common/constants'; import { useKibana } from '../common/lib/kibana'; import type { ESTermQuery } from '../../common/typed_json'; import { useErrorToast } from '../common/hooks/use_error_toast'; @@ -63,7 +64,7 @@ export const useLiveQueryDetails = ({ return useQuery<{ data: LiveQueryDetailsItem }, Error, LiveQueryDetailsItem>( ['liveQueries', { actionId, filterQuery, queryIds }], - () => http.get(`/api/osquery/live_queries/${actionId}`), + () => http.get(`/api/osquery/live_queries/${actionId}`, { version: API_VERSIONS.public.v1 }), { enabled: !skip && !!actionId, refetchInterval: isLive ? 5000 : false, 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 37f1789a964de..acc36d4961c30 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 @@ -10,6 +10,7 @@ import { useQuery } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; import type { GetAgentPoliciesResponseItem } from '@kbn/fleet-plugin/common'; +import { API_VERSIONS } from '../../common/constants'; import { useKibana } from '../common/lib/kibana'; import { useErrorToast } from '../common/hooks/use_error_toast'; @@ -24,19 +25,26 @@ export const useAgentPolicies = () => { agentPoliciesById: Record; agentPolicies: GetAgentPoliciesResponseItem[]; } - >(['agentPolicies'], () => http.get('/internal/osquery/fleet_wrapper/agent_policies'), { - initialData: [], - keepPreviousData: true, - select: (response) => ({ - agentPoliciesById: mapKeys(response, 'id'), - agentPolicies: response, - }), - onSuccess: () => setErrorToast(), - onError: (error) => - setErrorToast(error as Error, { - title: i18n.translate('xpack.osquery.agent_policies.fetchError', { - defaultMessage: 'Error while fetching agent policies', - }), + >( + ['agentPolicies'], + () => + http.get('/internal/osquery/fleet_wrapper/agent_policies', { + version: API_VERSIONS.internal.v1, + }), + { + initialData: [], + keepPreviousData: true, + select: (response) => ({ + agentPoliciesById: mapKeys(response, 'id'), + agentPolicies: response, }), - }); + onSuccess: () => setErrorToast(), + onError: (error) => + setErrorToast(error as Error, { + title: i18n.translate('xpack.osquery.agent_policies.fetchError', { + defaultMessage: 'Error while fetching agent policies', + }), + }), + } + ); }; 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 7a819e1118ad2..6fe6db18e2f2d 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 @@ -9,6 +9,7 @@ import { useQuery } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; import type { AgentPolicy } from '@kbn/fleet-plugin/common'; +import { API_VERSIONS } from '../../common/constants'; import { useKibana } from '../common/lib/kibana'; import { useErrorToast } from '../common/hooks/use_error_toast'; @@ -24,7 +25,10 @@ export const useAgentPolicy = ({ policyId, skip, silent }: UseAgentPolicy) => { return useQuery<{ item: AgentPolicy }, Error, AgentPolicy>( ['agentPolicy', { policyId }], - () => http.get(`/internal/osquery/fleet_wrapper/agent_policies/${policyId}`), + () => + http.get(`/internal/osquery/fleet_wrapper/agent_policies/${policyId}`, { + version: API_VERSIONS.internal.v1, + }), { enabled: !!(policyId && !skip), keepPreviousData: true, diff --git a/x-pack/plugins/osquery/public/agents/use_agent_details.ts b/x-pack/plugins/osquery/public/agents/use_agent_details.ts index d75987f38e93b..57110d6fa1f7d 100644 --- a/x-pack/plugins/osquery/public/agents/use_agent_details.ts +++ b/x-pack/plugins/osquery/public/agents/use_agent_details.ts @@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n'; import { useQuery } from '@tanstack/react-query'; import type { GetOneAgentResponse } from '@kbn/fleet-plugin/common'; +import { API_VERSIONS } from '../../common/constants'; import { useErrorToast } from '../common/hooks/use_error_toast'; import { useKibana } from '../common/lib/kibana'; @@ -24,7 +25,10 @@ export const useAgentDetails = ({ agentId, silent, skip }: UseAgentDetails) => { return useQuery( ['agentDetails', agentId], - () => http.get(`/internal/osquery/fleet_wrapper/agents/${agentId}`), + () => + http.get(`/internal/osquery/fleet_wrapper/agents/${agentId}`, { + version: API_VERSIONS.internal.v1, + }), { enabled: !skip, retry: false, diff --git a/x-pack/plugins/osquery/public/agents/use_agent_policies.ts b/x-pack/plugins/osquery/public/agents/use_agent_policies.ts index 737a64226c2bc..235789ef548a3 100644 --- a/x-pack/plugins/osquery/public/agents/use_agent_policies.ts +++ b/x-pack/plugins/osquery/public/agents/use_agent_policies.ts @@ -10,6 +10,7 @@ import type { UseQueryResult } from '@tanstack/react-query'; import { useQueries } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; import type { GetOneAgentPolicyResponse } from '@kbn/fleet-plugin/common'; +import { API_VERSIONS } from '../../common/constants'; import { useKibana } from '../common/lib/kibana'; import { useErrorToast } from '../common/hooks/use_error_toast'; @@ -20,7 +21,10 @@ export const useAgentPolicies = (policyIds: string[] = []) => { const agentResponse = useQueries({ queries: policyIds.map((policyId) => ({ queryKey: ['agentPolicy', policyId], - queryFn: () => http.get(`/internal/osquery/fleet_wrapper/agent_policies/${policyId}`), + queryFn: () => + http.get(`/internal/osquery/fleet_wrapper/agent_policies/${policyId}`, { + version: API_VERSIONS.internal.v1, + }), enabled: policyIds.length > 0, onSuccess: () => setErrorToast(), 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 cd30baf32d52d..3f03122bcbb8b 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 @@ -11,6 +11,7 @@ import { useQuery } from '@tanstack/react-query'; import type { Agent } from '@kbn/fleet-plugin/common'; import { AGENTS_PREFIX } from '@kbn/fleet-plugin/common'; +import { API_VERSIONS } from '../../common/constants'; import { useErrorToast } from '../common/hooks/use_error_toast'; import { useKibana } from '../common/lib/kibana'; @@ -34,6 +35,7 @@ export const useAgentPolicyAgentIds = ({ const kuery = `${AGENTS_PREFIX}.policy_id:${agentPolicyId}`; return http.get(`/internal/osquery/fleet_wrapper/agents`, { + version: API_VERSIONS.internal.v1, query: { kuery, perPage: 10000, 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 bc0d4e6323f62..a83f4116933a3 100644 --- a/x-pack/plugins/osquery/public/agents/use_agent_status.ts +++ b/x-pack/plugins/osquery/public/agents/use_agent_status.ts @@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n'; import { useQuery } from '@tanstack/react-query'; import type { GetAgentStatusResponse } from '@kbn/fleet-plugin/common'; +import { API_VERSIONS } from '../../common/constants'; import { useErrorToast } from '../common/hooks/use_error_toast'; import { useKibana } from '../common/lib/kibana'; @@ -24,16 +25,16 @@ export const useAgentStatus = ({ policyId, skip }: UseAgentStatus) => { return useQuery( ['agentStatus', policyId], () => - http.get( - `/internal/osquery/fleet_wrapper/agent_status`, - policyId + http.get(`/internal/osquery/fleet_wrapper/agent_status`, { + version: API_VERSIONS.internal.v1, + ...(policyId ? { query: { policyId, }, } - : {} - ), + : {}), + }), { enabled: !skip, select: (response) => response.results, 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 4f2aa8826f85f..98985b1c38505 100644 --- a/x-pack/plugins/osquery/public/agents/use_all_agents.ts +++ b/x-pack/plugins/osquery/public/agents/use_all_agents.ts @@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n'; import { useQuery } from '@tanstack/react-query'; import type { ListResult, Agent } from '@kbn/fleet-plugin/common'; +import { API_VERSIONS } from '../../common/constants'; import { useErrorToast } from '../common/hooks/use_error_toast'; import { useKibana } from '../common/lib/kibana'; import { useOsqueryPolicies } from './use_osquery_policies'; @@ -40,6 +41,7 @@ export const useAllAgents = (searchValue = '', opts: RequestOptions = { perPage: } return http.get(`/internal/osquery/fleet_wrapper/agents`, { + version: API_VERSIONS.internal.v1, query: { kuery, perPage, 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 589deab5334b1..52fa6e4da10ea 100644 --- a/x-pack/plugins/osquery/public/agents/use_osquery_policies.ts +++ b/x-pack/plugins/osquery/public/agents/use_osquery_policies.ts @@ -8,6 +8,7 @@ import { uniq } from 'lodash'; import { useQuery } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; +import { API_VERSIONS } from '../../common/constants'; import { useKibana } from '../common/lib/kibana'; import { useErrorToast } from '../common/hooks/use_error_toast'; @@ -19,7 +20,8 @@ export const useOsqueryPolicies = () => { ['osqueryPolicies'], () => http.get<{ items: Array<{ policy_id: string }> }>( - '/internal/osquery/fleet_wrapper/package_policies' + '/internal/osquery/fleet_wrapper/package_policies', + { version: API_VERSIONS.internal.v1 } ), { select: (response) => uniq(response.items.map((p) => p.policy_id)), 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 ba0e60e937a2b..3a111eaafe3bf 100644 --- a/x-pack/plugins/osquery/public/assets/use_assets_status.ts +++ b/x-pack/plugins/osquery/public/assets/use_assets_status.ts @@ -7,6 +7,7 @@ import type { KibanaAssetReference } from '@kbn/fleet-plugin/common'; import { useQuery } from '@tanstack/react-query'; +import { API_VERSIONS } from '../../common/constants'; import { useKibana } from '../common/lib/kibana'; import { INTEGRATION_ASSETS_STATUS_ID } from './constants'; @@ -17,8 +18,12 @@ export const useAssetsStatus = () => { install: KibanaAssetReference[]; update: KibanaAssetReference[]; upToDate: KibanaAssetReference[]; - }>([INTEGRATION_ASSETS_STATUS_ID], () => http.get('/internal/osquery/assets'), { - keepPreviousData: true, - retry: false, - }); + }>( + [INTEGRATION_ASSETS_STATUS_ID], + () => http.get('/internal/osquery/assets', { version: API_VERSIONS.internal.v1 }), + { + keepPreviousData: true, + retry: false, + } + ); }; 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 4ae6d8549d6fa..6740df46ee936 100644 --- a/x-pack/plugins/osquery/public/assets/use_import_assets.ts +++ b/x-pack/plugins/osquery/public/assets/use_import_assets.ts @@ -6,6 +6,7 @@ */ import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { API_VERSIONS } from '../../common/constants'; import { useKibana } from '../common/lib/kibana'; import { useErrorToast } from '../common/hooks/use_error_toast'; import { PACKS_ID } from '../packs/constants'; @@ -23,15 +24,18 @@ export const useImportAssets = ({ successToastText }: UseImportAssetsProps) => { } = useKibana().services; const setErrorToast = useErrorToast(); - return useMutation(() => http.post('/internal/osquery/assets/update'), { - onSuccess: () => { - setErrorToast(); - queryClient.invalidateQueries([PACKS_ID]); - queryClient.invalidateQueries([INTEGRATION_ASSETS_STATUS_ID]); - toasts.addSuccess(successToastText); - }, - onError: (error) => { - setErrorToast(error); - }, - }); + return useMutation( + () => http.post('/internal/osquery/assets/update', { version: API_VERSIONS.internal.v1 }), + { + onSuccess: () => { + setErrorToast(); + queryClient.invalidateQueries([PACKS_ID]); + queryClient.invalidateQueries([INTEGRATION_ASSETS_STATUS_ID]); + toasts.addSuccess(successToastText); + }, + onError: (error) => { + setErrorToast(error); + }, + } + ); }; 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 4602b8df24d78..4f34515c3b1fd 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 @@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n'; import { useQuery } from '@tanstack/react-query'; +import { API_VERSIONS } from '../../../common/constants'; import { useKibana } from '../lib/kibana'; import { useErrorToast } from './use_error_toast'; @@ -19,7 +20,8 @@ export const useOsqueryIntegrationStatus = () => { ['integration'], () => http.get<{ name: string; version: string; title: string; install_status: string }>( - '/internal/osquery/status' + '/internal/osquery/status', + { version: API_VERSIONS.internal.v1 } ), { onError: (error: Error) => diff --git a/x-pack/plugins/osquery/public/fleet_integration/use_fetch_status.tsx b/x-pack/plugins/osquery/public/fleet_integration/use_fetch_status.tsx index 051897eca1259..120d539620af1 100644 --- a/x-pack/plugins/osquery/public/fleet_integration/use_fetch_status.tsx +++ b/x-pack/plugins/osquery/public/fleet_integration/use_fetch_status.tsx @@ -6,6 +6,7 @@ */ import { useState, useEffect } from 'react'; +import { API_VERSIONS } from '../../common/constants'; import { useKibana } from '../common/lib/kibana'; export const useFetchStatus = () => { @@ -17,7 +18,9 @@ export const useFetchStatus = () => { useEffect(() => { const fetchStatus = () => { http - .get<{ install_status: string }>('/internal/osquery/status') + .get<{ install_status: string }>('/internal/osquery/status', { + version: API_VERSIONS.internal.v1, + }) .then((response) => { setLoading(false); setDisabled(response?.install_status !== 'installed'); 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 51c7f4ad272c0..0fa7c538e6737 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 @@ -7,6 +7,7 @@ import { useMutation } from '@tanstack/react-query'; import type { AgentSelection } from '@kbn/osquery-io-ts-types'; +import { API_VERSIONS } from '../../common/constants'; import type { CreateLiveQueryRequestBodySchema } from '../../common/schemas/routes/live_query'; import { useKibana } from '../common/lib/kibana'; import { useErrorToast } from '../common/hooks/use_error_toast'; @@ -36,6 +37,7 @@ export const useCreateLiveQuery = ({ onSuccess }: UseLiveQueryProps) => { const response = await http.post<{ data: LiveQueryDetailsItem }>( '/api/osquery/live_queries', { + version: API_VERSIONS.public.v1, body: JSON.stringify({ ...payload, agent_all: agentSelection.allAgentsSelected, 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 9c81f71e441f2..b96732af1cd77 100644 --- a/x-pack/plugins/osquery/public/packs/use_create_pack.ts +++ b/x-pack/plugins/osquery/public/packs/use_create_pack.ts @@ -8,6 +8,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; +import { API_VERSIONS } from '../../common/constants'; import { useKibana } from '../common/lib/kibana'; import { PLUGIN_ID } from '../../common'; import { pagePathGetters } from '../common/page_paths'; @@ -35,6 +36,7 @@ export const useCreatePack = ({ withRedirect }: UseCreatePackProps) => { >( (payload) => http.post('/api/osquery/packs', { + version: API_VERSIONS.public.v1, body: JSON.stringify(payload), }), { 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 8b5e7f0fb1450..31c9af2e3a21a 100644 --- a/x-pack/plugins/osquery/public/packs/use_delete_pack.ts +++ b/x-pack/plugins/osquery/public/packs/use_delete_pack.ts @@ -8,6 +8,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; +import { API_VERSIONS } from '../../common/constants'; import { useKibana } from '../common/lib/kibana'; import { PLUGIN_ID } from '../../common'; import { pagePathGetters } from '../common/page_paths'; @@ -28,24 +29,30 @@ export const useDeletePack = ({ packId, withRedirect }: UseDeletePackProps) => { } = useKibana().services; const setErrorToast = useErrorToast(); - return useMutation(() => http.delete(`/api/osquery/packs/${packId}`), { - onError: (error: { body: { error: string; message: string } }) => { - setErrorToast(error, { - title: error.body.error, - toastMessage: error.body.message, - }); - }, - onSuccess: () => { - queryClient.invalidateQueries([PACKS_ID]); - if (withRedirect) { - navigateToApp(PLUGIN_ID, { path: pagePathGetters.packs() }); - } + return useMutation( + () => + http.delete(`/api/osquery/packs/${packId}`, { + version: API_VERSIONS.public.v1, + }), + { + onError: (error: { body: { error: string; message: string } }) => { + setErrorToast(error, { + title: error.body.error, + toastMessage: error.body.message, + }); + }, + onSuccess: () => { + queryClient.invalidateQueries([PACKS_ID]); + if (withRedirect) { + navigateToApp(PLUGIN_ID, { path: pagePathGetters.packs() }); + } - toasts.addSuccess( - i18n.translate('xpack.osquery.deletePack.successToastMessageText', { - defaultMessage: 'Successfully deleted pack', - }) - ); - }, - }); + toasts.addSuccess( + i18n.translate('xpack.osquery.deletePack.successToastMessageText', { + defaultMessage: 'Successfully deleted pack', + }) + ); + }, + } + ); }; diff --git a/x-pack/plugins/osquery/public/packs/use_pack.ts b/x-pack/plugins/osquery/public/packs/use_pack.ts index 9fac2175cc1b2..db8447c6b00a9 100644 --- a/x-pack/plugins/osquery/public/packs/use_pack.ts +++ b/x-pack/plugins/osquery/public/packs/use_pack.ts @@ -6,6 +6,7 @@ */ import { useQuery } from '@tanstack/react-query'; +import { API_VERSIONS } from '../../common/constants'; import { useKibana } from '../common/lib/kibana'; import type { PackItem } from './types'; @@ -19,7 +20,7 @@ export const usePack = ({ packId, skip = false }: UsePack) => { return useQuery<{ data: PackItem }, unknown, PackItem>( ['pack', { packId }], - () => http.get(`/api/osquery/packs/${packId}`), + () => http.get(`/api/osquery/packs/${packId}`, { version: API_VERSIONS.public.v1 }), { select: (response) => response?.data, keepPreviousData: true, diff --git a/x-pack/plugins/osquery/public/packs/use_packs.ts b/x-pack/plugins/osquery/public/packs/use_packs.ts index afe612094f660..aac8cc204cb9e 100644 --- a/x-pack/plugins/osquery/public/packs/use_packs.ts +++ b/x-pack/plugins/osquery/public/packs/use_packs.ts @@ -7,6 +7,7 @@ import { useQuery } from '@tanstack/react-query'; +import { API_VERSIONS } from '../../common/constants'; import { useKibana } from '../common/lib/kibana'; import { PACKS_ID } from './constants'; import type { PackSavedObject } from './types'; @@ -29,6 +30,7 @@ export const usePacks = ({ [PACKS_ID, { pageIndex, pageSize, sortField, sortOrder }], () => http.get('/api/osquery/packs', { + version: API_VERSIONS.public.v1, query: { pageIndex, pageSize, sortField, sortOrder }, }), { 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 c5d7fc8b43210..6597cbe215f51 100644 --- a/x-pack/plugins/osquery/public/packs/use_update_pack.ts +++ b/x-pack/plugins/osquery/public/packs/use_update_pack.ts @@ -9,6 +9,7 @@ import type { UseMutationOptions } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; +import { API_VERSIONS } from '../../common/constants'; import { useKibana } from '../common/lib/kibana'; import { PLUGIN_ID } from '../../common'; import { pagePathGetters } from '../common/page_paths'; @@ -41,6 +42,7 @@ export const useUpdatePack = ({ withRedirect, options }: UseUpdatePackProps) => >( ({ id, ...payload }) => http.put(`/api/osquery/packs/${id}`, { + version: API_VERSIONS.public.v1, body: JSON.stringify(payload), }), { diff --git a/x-pack/plugins/osquery/public/saved_queries/use_create_saved_query.ts b/x-pack/plugins/osquery/public/saved_queries/use_create_saved_query.ts index ce79fb25ace3e..94fcccc133793 100644 --- a/x-pack/plugins/osquery/public/saved_queries/use_create_saved_query.ts +++ b/x-pack/plugins/osquery/public/saved_queries/use_create_saved_query.ts @@ -8,6 +8,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; +import { API_VERSIONS } from '../../common/constants'; import { useKibana } from '../common/lib/kibana'; import { PLUGIN_ID } from '../../common'; import { pagePathGetters } from '../common/page_paths'; @@ -36,6 +37,7 @@ export const useCreateSavedQuery = ({ withRedirect }: UseCreateSavedQueryProps) >( (payload) => http.post('/api/osquery/saved_queries', { + version: API_VERSIONS.public.v1, body: JSON.stringify(payload), }), { 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 f03ac709db1ee..58ef47ff683aa 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 @@ -8,6 +8,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; +import { API_VERSIONS } from '../../common/constants'; import { useKibana } from '../common/lib/kibana'; import { PLUGIN_ID } from '../../common'; import { pagePathGetters } from '../common/page_paths'; @@ -27,21 +28,27 @@ export const useDeleteSavedQuery = ({ savedQueryId }: UseDeleteSavedQueryProps) } = useKibana().services; const setErrorToast = useErrorToast(); - return useMutation(() => http.delete(`/api/osquery/saved_queries/${savedQueryId}`), { - onError: (error: { body: { error: string; message: string } }) => { - setErrorToast(error, { - title: error.body.error, - toastMessage: error.body.message, - }); - }, - onSuccess: () => { - queryClient.invalidateQueries([SAVED_QUERIES_ID]); - navigateToApp(PLUGIN_ID, { path: pagePathGetters.saved_queries() }); - toasts.addSuccess( - i18n.translate('xpack.osquery.editSavedQuery.deleteSuccessToastMessageText', { - defaultMessage: 'Successfully deleted saved query', - }) - ); - }, - }); + return useMutation( + () => + http.delete(`/api/osquery/saved_queries/${savedQueryId}`, { + version: API_VERSIONS.public.v1, + }), + { + onError: (error: { body: { error: string; message: string } }) => { + setErrorToast(error, { + title: error.body.error, + toastMessage: error.body.message, + }); + }, + onSuccess: () => { + queryClient.invalidateQueries([SAVED_QUERIES_ID]); + navigateToApp(PLUGIN_ID, { path: pagePathGetters.saved_queries() }); + toasts.addSuccess( + i18n.translate('xpack.osquery.editSavedQuery.deleteSuccessToastMessageText', { + defaultMessage: 'Successfully deleted saved query', + }) + ); + }, + } + ); }; 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 f56bd9c0ec010..955987a353ca8 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 @@ -7,6 +7,7 @@ import { useQuery } from '@tanstack/react-query'; +import { API_VERSIONS } from '../../common/constants'; import { useKibana } from '../common/lib/kibana'; import { useErrorToast } from '../common/hooks/use_error_toast'; import { SAVED_QUERIES_ID } from './constants'; @@ -34,6 +35,7 @@ export const useSavedQueries = ({ [SAVED_QUERIES_ID, { pageIndex, pageSize, sortField, sortOrder }], () => http.get('/api/osquery/saved_queries', { + version: API_VERSIONS.public.v1, query: { page: pageIndex + 1, pageSize, sort: sortField, sortOrder }, }), { 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 96192c2b7abdc..d7ae357503863 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 @@ -7,6 +7,7 @@ import { useQuery } from '@tanstack/react-query'; +import { API_VERSIONS } from '../../common/constants'; import { PLUGIN_ID } from '../../common'; import { useKibana } from '../common/lib/kibana'; import { pagePathGetters } from '../common/page_paths'; @@ -36,7 +37,8 @@ export const useSavedQuery = ({ savedQueryId }: UseSavedQueryProps) => { SavedQuerySO & { error?: { error: string; message: string } } >( [SAVED_QUERY_ID, { savedQueryId }], - () => http.get(`/api/osquery/saved_queries/${savedQueryId}`), + () => + http.get(`/api/osquery/saved_queries/${savedQueryId}`, { version: API_VERSIONS.public.v1 }), { keepPreviousData: true, refetchOnWindowFocus: false, 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 58fcc06740c45..f6707257f4721 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 @@ -8,6 +8,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { i18n } from '@kbn/i18n'; +import { API_VERSIONS } from '../../common/constants'; import { useKibana } from '../common/lib/kibana'; import { PLUGIN_ID } from '../../common'; import { pagePathGetters } from '../common/page_paths'; @@ -31,6 +32,7 @@ export const useUpdateSavedQuery = ({ savedQueryId }: UseUpdateSavedQueryProps) return useMutation( (payload) => http.put(`/api/osquery/saved_queries/${savedQueryId}`, { + version: API_VERSIONS.public.v1, body: JSON.stringify(payload), }), { diff --git a/x-pack/plugins/osquery/public/shared_components/osquery_action/use_is_osquery_available_simple.tsx b/x-pack/plugins/osquery/public/shared_components/osquery_action/use_is_osquery_available_simple.tsx index 574ca04480c69..4e13d02ef4e75 100644 --- a/x-pack/plugins/osquery/public/shared_components/osquery_action/use_is_osquery_available_simple.tsx +++ b/x-pack/plugins/osquery/public/shared_components/osquery_action/use_is_osquery_available_simple.tsx @@ -9,6 +9,7 @@ import { useEffect, useState } from 'react'; import { find } from 'lodash'; import type { AgentPolicy, FleetServerAgent, NewPackagePolicy } from '@kbn/fleet-plugin/common'; +import { API_VERSIONS } from '../../../common/constants'; import { useKibana } from '../../common/lib/kibana'; import { OSQUERY_INTEGRATION_NAME } from '../../../common'; @@ -23,10 +24,12 @@ export const useIsOsqueryAvailableSimple = ({ agentId }: IProps) => { (async () => { try { const { item: agentInfo }: { item: FleetServerAgent } = await http.get( - `/internal/osquery/fleet_wrapper/agents/${agentId}` + `/internal/osquery/fleet_wrapper/agents/${agentId}`, + { version: API_VERSIONS.internal.v1 } ); const { item: packageInfo }: { item: AgentPolicy } = await http.get( - `/internal/osquery/fleet_wrapper/agent_policies/${agentInfo.policy_id}` + `/internal/osquery/fleet_wrapper/agent_policies/${agentInfo.policy_id}`, + { version: API_VERSIONS.internal.v1 } ); const osqueryPackageInstalled = find(packageInfo?.package_policies, [ 'package.name', diff --git a/x-pack/plugins/osquery/server/routes/asset/get_assets_status_route.ts b/x-pack/plugins/osquery/server/routes/asset/get_assets_status_route.ts index c40e1488ca11e..a76ec52350ade 100644 --- a/x-pack/plugins/osquery/server/routes/asset/get_assets_status_route.ts +++ b/x-pack/plugins/osquery/server/routes/asset/get_assets_status_route.ts @@ -11,89 +11,97 @@ import { asyncForEach } from '@kbn/std'; import type { IRouter } from '@kbn/core/server'; import type { KibanaAssetReference } from '@kbn/fleet-plugin/common'; +import { API_VERSIONS } from '../../../common/constants'; import { packAssetSavedObjectType, packSavedObjectType } from '../../../common/types'; import { PLUGIN_ID, OSQUERY_INTEGRATION_NAME } from '../../../common'; import type { OsqueryAppContext } from '../../lib/osquery_app_context_services'; export const getAssetsStatusRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { - router.get( - { + router.versioned + .get({ + access: 'internal', path: '/internal/osquery/assets', - validate: { - params: schema.object({}, { unknowns: 'allow' }), - }, options: { tags: [`access:${PLUGIN_ID}-writePacks`] }, - }, - async (context, request, response) => { - const savedObjectsClient = (await context.core).savedObjects.client; + }) + .addVersion( + { + version: API_VERSIONS.internal.v1, + validate: { + request: { + params: schema.object({}, { unknowns: 'allow' }), + }, + }, + }, + async (context, request, response) => { + const savedObjectsClient = (await context.core).savedObjects.client; - let installation; + let installation; - try { - installation = await osqueryContext.service - .getPackageService() - ?.asInternalUser?.getInstallation(OSQUERY_INTEGRATION_NAME); - } catch (err) { - return response.notFound(); - } + try { + installation = await osqueryContext.service + .getPackageService() + ?.asInternalUser?.getInstallation(OSQUERY_INTEGRATION_NAME); + } catch (err) { + return response.notFound(); + } - if (installation) { - const installationPackAssets = filter( - ['type', packAssetSavedObjectType], - installation.installed_kibana - ); + if (installation) { + const installationPackAssets = filter( + ['type', packAssetSavedObjectType], + installation.installed_kibana + ); - const install: KibanaAssetReference[] = []; - const update: KibanaAssetReference[] = []; - const upToDate: KibanaAssetReference[] = []; + const install: KibanaAssetReference[] = []; + const update: KibanaAssetReference[] = []; + const upToDate: KibanaAssetReference[] = []; - await asyncForEach(installationPackAssets, async (installationPackAsset) => { - const isInstalled = await savedObjectsClient.find<{ version: number }>({ - type: packSavedObjectType, - hasReference: { - type: installationPackAsset.type, - id: installationPackAsset.id, - }, - }); + await asyncForEach(installationPackAssets, async (installationPackAsset) => { + const isInstalled = await savedObjectsClient.find<{ version: number }>({ + type: packSavedObjectType, + hasReference: { + type: installationPackAsset.type, + id: installationPackAsset.id, + }, + }); - if (!isInstalled.total) { - install.push(installationPackAsset); - } + if (!isInstalled.total) { + install.push(installationPackAsset); + } - if (isInstalled.total) { - const packAssetSavedObject = await savedObjectsClient.get<{ version: number }>( - installationPackAsset.type, - installationPackAsset.id - ); + if (isInstalled.total) { + const packAssetSavedObject = await savedObjectsClient.get<{ version: number }>( + installationPackAsset.type, + installationPackAsset.id + ); - if (packAssetSavedObject) { - if ( - !packAssetSavedObject.attributes.version || - !isInstalled.saved_objects[0].attributes.version - ) { - install.push(installationPackAsset); - } else if ( - packAssetSavedObject.attributes.version > - isInstalled.saved_objects[0].attributes.version - ) { - update.push(installationPackAsset); - } else { - upToDate.push(installationPackAsset); + if (packAssetSavedObject) { + if ( + !packAssetSavedObject.attributes.version || + !isInstalled.saved_objects[0].attributes.version + ) { + install.push(installationPackAsset); + } else if ( + packAssetSavedObject.attributes.version > + isInstalled.saved_objects[0].attributes.version + ) { + update.push(installationPackAsset); + } else { + upToDate.push(installationPackAsset); + } } } - } - }); + }); - return response.ok({ - body: { - install, - update, - upToDate, - }, - }); - } + return response.ok({ + body: { + install, + update, + upToDate, + }, + }); + } - return response.ok(); - } - ); + return response.ok(); + } + ); }; diff --git a/x-pack/plugins/osquery/server/routes/asset/update_assets_route.ts b/x-pack/plugins/osquery/server/routes/asset/update_assets_route.ts index dbc1844cd7d63..5cfef0d84c289 100644 --- a/x-pack/plugins/osquery/server/routes/asset/update_assets_route.ts +++ b/x-pack/plugins/osquery/server/routes/asset/update_assets_route.ts @@ -13,6 +13,7 @@ import deepmerge from 'deepmerge'; import type { IRouter } from '@kbn/core/server'; import type { KibanaAssetReference } from '@kbn/fleet-plugin/common'; +import { API_VERSIONS } from '../../../common/constants'; import { packAssetSavedObjectType, packSavedObjectType } from '../../../common/types'; import { combineMerge } from './utils'; import { PLUGIN_ID, OSQUERY_INTEGRATION_NAME } from '../../../common'; @@ -21,184 +22,191 @@ import { convertSOQueriesToPack, convertPackQueriesToSO } from '../pack/utils'; import type { PackSavedObject } from '../../common/types'; export const updateAssetsRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { - router.post( - { + router.versioned + .post({ + access: 'internal', path: '/internal/osquery/assets/update', - validate: { - params: schema.object({}, { unknowns: 'allow' }), - }, options: { tags: [`access:${PLUGIN_ID}-writePacks`] }, - }, - async (context, request, response) => { - const savedObjectsClient = (await context.core).savedObjects.client; - const currentUser = await osqueryContext.security.authc.getCurrentUser(request)?.username; - - let installation; - - try { - installation = await osqueryContext.service - .getPackageService() - ?.asInternalUser?.getInstallation(OSQUERY_INTEGRATION_NAME); - } catch (err) { - return response.notFound(); - } + }) + .addVersion( + { + version: API_VERSIONS.internal.v1, + validate: { + request: { + params: schema.object({}, { unknowns: 'allow' }), + }, + }, + }, + async (context, request, response) => { + const savedObjectsClient = (await context.core).savedObjects.client; + const currentUser = await osqueryContext.security.authc.getCurrentUser(request)?.username; + + let installation; + + try { + installation = await osqueryContext.service + .getPackageService() + ?.asInternalUser?.getInstallation(OSQUERY_INTEGRATION_NAME); + } catch (err) { + return response.notFound(); + } + + if (installation) { + const installationPackAssets = filter(installation.installed_kibana, [ + 'type', + packAssetSavedObjectType, + ]); + + const install: KibanaAssetReference[] = []; + const update: KibanaAssetReference[] = []; + const upToDate: KibanaAssetReference[] = []; + + await asyncForEach(installationPackAssets, async (installationPackAsset) => { + const isInstalled = await savedObjectsClient.find<{ version: number }>({ + type: packSavedObjectType, + hasReference: { + type: installationPackAsset.type, + id: installationPackAsset.id, + }, + }); - if (installation) { - const installationPackAssets = filter(installation.installed_kibana, [ - 'type', - packAssetSavedObjectType, - ]); - - const install: KibanaAssetReference[] = []; - const update: KibanaAssetReference[] = []; - const upToDate: KibanaAssetReference[] = []; - - await asyncForEach(installationPackAssets, async (installationPackAsset) => { - const isInstalled = await savedObjectsClient.find<{ version: number }>({ - type: packSavedObjectType, - hasReference: { - type: installationPackAsset.type, - id: installationPackAsset.id, - }, - }); + if (!isInstalled.total) { + install.push(installationPackAsset); + } - if (!isInstalled.total) { - install.push(installationPackAsset); - } - - if (isInstalled.total) { - const packAssetSavedObject = await savedObjectsClient.get<{ version: number }>( - installationPackAsset.type, - installationPackAsset.id - ); - - if (packAssetSavedObject) { - if ( - !packAssetSavedObject.attributes.version || - !isInstalled.saved_objects[0].attributes.version - ) { - install.push(installationPackAsset); - } else if ( - packAssetSavedObject.attributes.version > - isInstalled.saved_objects[0].attributes.version - ) { - update.push(installationPackAsset); - } else { - upToDate.push(installationPackAsset); + if (isInstalled.total) { + const packAssetSavedObject = await savedObjectsClient.get<{ version: number }>( + installationPackAsset.type, + installationPackAsset.id + ); + + if (packAssetSavedObject) { + if ( + !packAssetSavedObject.attributes.version || + !isInstalled.saved_objects[0].attributes.version + ) { + install.push(installationPackAsset); + } else if ( + packAssetSavedObject.attributes.version > + isInstalled.saved_objects[0].attributes.version + ) { + update.push(installationPackAsset); + } else { + upToDate.push(installationPackAsset); + } } } - } - }); + }); - await Promise.all([ - ...install.map(async (installationPackAsset) => { - const packAssetSavedObject = await savedObjectsClient.get( - installationPackAsset.type, - installationPackAsset.id - ); + await Promise.all([ + ...install.map(async (installationPackAsset) => { + const packAssetSavedObject = await savedObjectsClient.get( + installationPackAsset.type, + installationPackAsset.id + ); - const conflictingEntries = await savedObjectsClient.find({ - type: packSavedObjectType, - filter: `${packSavedObjectType}.attributes.name: "${packAssetSavedObject.attributes.name}"`, - }); + const conflictingEntries = await savedObjectsClient.find({ + type: packSavedObjectType, + filter: `${packSavedObjectType}.attributes.name: "${packAssetSavedObject.attributes.name}"`, + }); + + const name = + conflictingEntries.saved_objects.length && + some(conflictingEntries.saved_objects, [ + 'attributes.name', + packAssetSavedObject.attributes.name, + ]) + ? `${packAssetSavedObject.attributes.name}-elastic` + : packAssetSavedObject.attributes.name; + + await savedObjectsClient.create( + packSavedObjectType, + { + name, + description: packAssetSavedObject.attributes.description, + queries: packAssetSavedObject.attributes.queries, + enabled: false, + created_at: moment().toISOString(), + created_by: currentUser, + updated_at: moment().toISOString(), + updated_by: currentUser, + version: packAssetSavedObject.attributes.version ?? 1, + }, + { + references: [ + ...packAssetSavedObject.references, + { + type: packAssetSavedObject.type, + id: packAssetSavedObject.id, + name: packAssetSavedObject.attributes.name, + }, + ], + refresh: 'wait_for', + } + ); + }), + ...update.map(async (updatePackAsset) => { + const packAssetSavedObject = await savedObjectsClient.get( + updatePackAsset.type, + updatePackAsset.id + ); - const name = - conflictingEntries.saved_objects.length && - some(conflictingEntries.saved_objects, [ - 'attributes.name', - packAssetSavedObject.attributes.name, - ]) - ? `${packAssetSavedObject.attributes.name}-elastic` - : packAssetSavedObject.attributes.name; - - await savedObjectsClient.create( - packSavedObjectType, - { - name, - description: packAssetSavedObject.attributes.description, - queries: packAssetSavedObject.attributes.queries, - enabled: false, - created_at: moment().toISOString(), - created_by: currentUser, - updated_at: moment().toISOString(), - updated_by: currentUser, - version: packAssetSavedObject.attributes.version ?? 1, - }, - { - references: [ - ...packAssetSavedObject.references, - { - type: packAssetSavedObject.type, - id: packAssetSavedObject.id, - name: packAssetSavedObject.attributes.name, - }, - ], - refresh: 'wait_for', + const packSavedObjectsResponse = await savedObjectsClient.find({ + type: 'osquery-pack', + hasReference: { + type: updatePackAsset.type, + id: updatePackAsset.id, + }, + }); + + if (packSavedObjectsResponse.total) { + await savedObjectsClient.update( + packSavedObjectsResponse.saved_objects[0].type, + packSavedObjectsResponse.saved_objects[0].id, + deepmerge.all([ + omit(packSavedObjectsResponse.saved_objects[0].attributes, 'queries'), + omit(packAssetSavedObject.attributes, 'queries'), + { + updated_at: moment().toISOString(), + updated_by: currentUser, + queries: convertPackQueriesToSO( + deepmerge( + convertSOQueriesToPack( + packSavedObjectsResponse.saved_objects[0].attributes.queries + ), + convertSOQueriesToPack(packAssetSavedObject.attributes.queries), + { + arrayMerge: combineMerge, + } + ) + ), + }, + { + arrayMerge: combineMerge, + }, + ]), + { refresh: 'wait_for' } + ); } - ); - }), - ...update.map(async (updatePackAsset) => { - const packAssetSavedObject = await savedObjectsClient.get( - updatePackAsset.type, - updatePackAsset.id - ); - - const packSavedObjectsResponse = await savedObjectsClient.find({ - type: 'osquery-pack', - hasReference: { - type: updatePackAsset.type, - id: updatePackAsset.id, - }, - }); - - if (packSavedObjectsResponse.total) { - await savedObjectsClient.update( - packSavedObjectsResponse.saved_objects[0].type, - packSavedObjectsResponse.saved_objects[0].id, - deepmerge.all([ - omit(packSavedObjectsResponse.saved_objects[0].attributes, 'queries'), - omit(packAssetSavedObject.attributes, 'queries'), - { - updated_at: moment().toISOString(), - updated_by: currentUser, - queries: convertPackQueriesToSO( - deepmerge( - convertSOQueriesToPack( - packSavedObjectsResponse.saved_objects[0].attributes.queries - ), - convertSOQueriesToPack(packAssetSavedObject.attributes.queries), - { - arrayMerge: combineMerge, - } - ) - ), - }, - { - arrayMerge: combineMerge, - }, - ]), - { refresh: 'wait_for' } - ); - } - }), - ]); + }), + ]); + + return response.ok({ + body: { + install, + update, + upToDate, + }, + }); + } return response.ok({ body: { - install, - update, - upToDate, + install: 0, + update: 0, + upToDate: 0, }, }); } - - return response.ok({ - body: { - install: 0, - update: 0, - upToDate: 0, - }, - }); - } - ); + ); }; diff --git a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_details.ts b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_details.ts index 016675b9585d2..65622569a446e 100644 --- a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_details.ts +++ b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_details.ts @@ -7,31 +7,39 @@ import { schema } from '@kbn/config-schema'; import type { IRouter } from '@kbn/core/server'; +import { API_VERSIONS } from '../../../common/constants'; import { PLUGIN_ID } from '../../../common'; import type { OsqueryAppContext } from '../../lib/osquery_app_context_services'; export const getAgentDetailsRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { - router.get( - { + router.versioned + .get({ + access: 'internal', path: '/internal/osquery/fleet_wrapper/agents/{id}', - validate: { - params: schema.object({}, { unknowns: 'allow' }), - }, options: { tags: [`access:${PLUGIN_ID}-read`] }, - }, - async (context, request, response) => { - let agent; + }) + .addVersion( + { + version: API_VERSIONS.internal.v1, + validate: { + request: { + params: schema.object({}, { unknowns: 'allow' }), + }, + }, + }, + async (context, request, response) => { + let agent; - try { - agent = await osqueryContext.service - .getAgentService() - ?.asInternalUser // @ts-expect-error update types - ?.getAgent(request.params.id); - } catch (err) { - return response.notFound(); - } + try { + agent = await osqueryContext.service + .getAgentService() + ?.asInternalUser // @ts-expect-error update types + ?.getAgent(request.params.id); + } catch (err) { + return response.notFound(); + } - return response.ok({ body: { item: agent } }); - } - ); + return response.ok({ body: { item: agent } }); + } + ); }; diff --git a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policies.ts b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policies.ts index cf4ce574030a6..0fc992c69760b 100644 --- a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policies.ts +++ b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policies.ts @@ -12,57 +12,65 @@ import { satisfies } from 'semver'; import type { GetAgentPoliciesResponseItem, PackagePolicy } from '@kbn/fleet-plugin/common'; import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common'; import type { IRouter } from '@kbn/core/server'; +import { API_VERSIONS } from '../../../common/constants'; import { OSQUERY_INTEGRATION_NAME, PLUGIN_ID } from '../../../common'; import type { OsqueryAppContext } from '../../lib/osquery_app_context_services'; import { getInternalSavedObjectsClient } from '../utils'; export const getAgentPoliciesRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { - router.get( - { + router.versioned + .get({ + access: 'internal', path: '/internal/osquery/fleet_wrapper/agent_policies', - validate: { - params: schema.object({}, { unknowns: 'allow' }), - query: schema.object({}, { unknowns: 'allow' }), - }, options: { tags: [`access:${PLUGIN_ID}-read`] }, - }, - async (context, request, response) => { - const internalSavedObjectsClient = await getInternalSavedObjectsClient( - osqueryContext.getStartServices - ); - const agentService = osqueryContext.service.getAgentService(); - const agentPolicyService = osqueryContext.service.getAgentPolicyService(); - const packagePolicyService = osqueryContext.service.getPackagePolicyService(); + }) + .addVersion( + { + version: API_VERSIONS.internal.v1, + validate: { + request: { + params: schema.object({}, { unknowns: 'allow' }), + query: schema.object({}, { unknowns: 'allow' }), + }, + }, + }, + async (context, request, response) => { + const internalSavedObjectsClient = await getInternalSavedObjectsClient( + osqueryContext.getStartServices + ); + const agentService = osqueryContext.service.getAgentService(); + const agentPolicyService = osqueryContext.service.getAgentPolicyService(); + const packagePolicyService = osqueryContext.service.getPackagePolicyService(); + + const { items: packagePolicies } = (await packagePolicyService?.list( + internalSavedObjectsClient, + { + kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${OSQUERY_INTEGRATION_NAME}`, + perPage: 1000, + page: 1, + } + )) ?? { items: [] as PackagePolicy[] }; + const supportedPackagePolicyIds = filter(packagePolicies, (packagePolicy) => + satisfies(packagePolicy.package?.version ?? '', '>=0.6.0') + ); + const agentPolicyIds = uniq(map(supportedPackagePolicyIds, 'policy_id')); + const agentPolicies = await agentPolicyService?.getByIds( + internalSavedObjectsClient, + agentPolicyIds + ); - const { items: packagePolicies } = (await packagePolicyService?.list( - internalSavedObjectsClient, - { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${OSQUERY_INTEGRATION_NAME}`, - perPage: 1000, - page: 1, + if (agentPolicies?.length) { + await pMap( + agentPolicies, + (agentPolicy: GetAgentPoliciesResponseItem) => + agentService?.asInternalUser + .getAgentStatusForAgentPolicy(agentPolicy.id) + .then(({ total: agentTotal }) => (agentPolicy.agents = agentTotal)), + { concurrency: 10 } + ); } - )) ?? { items: [] as PackagePolicy[] }; - const supportedPackagePolicyIds = filter(packagePolicies, (packagePolicy) => - satisfies(packagePolicy.package?.version ?? '', '>=0.6.0') - ); - const agentPolicyIds = uniq(map(supportedPackagePolicyIds, 'policy_id')); - const agentPolicies = await agentPolicyService?.getByIds( - internalSavedObjectsClient, - agentPolicyIds - ); - if (agentPolicies?.length) { - await pMap( - agentPolicies, - (agentPolicy: GetAgentPoliciesResponseItem) => - agentService?.asInternalUser - .getAgentStatusForAgentPolicy(agentPolicy.id) - .then(({ total: agentTotal }) => (agentPolicy.agents = agentTotal)), - { concurrency: 10 } - ); + return response.ok({ body: agentPolicies }); } - - return response.ok({ body: agentPolicies }); - } - ); + ); }; diff --git a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policy.ts b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policy.ts index aa1855bbfaac3..b5fe59aa9d34d 100644 --- a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policy.ts +++ b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policy.ts @@ -7,30 +7,38 @@ import { schema } from '@kbn/config-schema'; import type { IRouter } from '@kbn/core/server'; +import { API_VERSIONS } from '../../../common/constants'; import { PLUGIN_ID } from '../../../common'; import type { OsqueryAppContext } from '../../lib/osquery_app_context_services'; import { getInternalSavedObjectsClient } from '../utils'; export const getAgentPolicyRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { - router.get( - { + router.versioned + .get({ + access: 'internal', path: '/internal/osquery/fleet_wrapper/agent_policies/{id}', - validate: { - params: schema.object({ - id: schema.string(), - }), - }, options: { tags: [`access:${PLUGIN_ID}-read`] }, - }, - async (context, request, response) => { - const internalSavedObjectsClient = await getInternalSavedObjectsClient( - osqueryContext.getStartServices - ); - const packageInfo = await osqueryContext.service - .getAgentPolicyService() - ?.get(internalSavedObjectsClient, request.params.id); + }) + .addVersion( + { + version: API_VERSIONS.internal.v1, + validate: { + request: { + params: schema.object({ + id: schema.string(), + }), + }, + }, + }, + async (context, request, response) => { + const internalSavedObjectsClient = await getInternalSavedObjectsClient( + osqueryContext.getStartServices + ); + const packageInfo = await osqueryContext.service + .getAgentPolicyService() + ?.get(internalSavedObjectsClient, request.params.id); - return response.ok({ body: { item: packageInfo } }); - } - ); + return response.ok({ body: { item: packageInfo } }); + } + ); }; diff --git a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_status_for_agent_policy.ts b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_status_for_agent_policy.ts index f59a2f38d6c86..61a7e24ddaa76 100644 --- a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_status_for_agent_policy.ts +++ b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_status_for_agent_policy.ts @@ -8,6 +8,7 @@ import { schema } from '@kbn/config-schema'; import type { GetAgentStatusResponse } from '@kbn/fleet-plugin/common'; import type { IRouter } from '@kbn/core/server'; +import { API_VERSIONS } from '../../../common/constants'; import { PLUGIN_ID } from '../../../common'; import type { OsqueryAppContext } from '../../lib/osquery_app_context_services'; @@ -15,31 +16,38 @@ export const getAgentStatusForAgentPolicyRoute = ( router: IRouter, osqueryContext: OsqueryAppContext ) => { - router.get( - { + router.versioned + .get({ + access: 'internal', path: '/internal/osquery/fleet_wrapper/agent_status', - validate: { - query: schema.object({ - policyId: schema.string(), - kuery: schema.maybe(schema.string()), - }), - params: schema.object({}, { unknowns: 'allow' }), - }, options: { tags: [`access:${PLUGIN_ID}-read`] }, - }, - async (context, request, response) => { - const results = await osqueryContext.service - .getAgentService() - ?.asScoped(request) - .getAgentStatusForAgentPolicy(request.query.policyId, request.query.kuery); + }) + .addVersion( + { + version: API_VERSIONS.internal.v1, + validate: { + request: { + query: schema.object({ + policyId: schema.string(), + kuery: schema.maybe(schema.string()), + }), + params: schema.object({}, { unknowns: 'allow' }), + }, + }, + }, + async (context, request, response) => { + const results = await osqueryContext.service + .getAgentService() + ?.asScoped(request) + .getAgentStatusForAgentPolicy(request.query.policyId, request.query.kuery); - if (!results) { - return response.ok({ body: {} }); - } + if (!results) { + return response.ok({ body: {} }); + } - const body: GetAgentStatusResponse = { results }; + const body: GetAgentStatusResponse = { results }; - return response.ok({ body }); - } - ); + return response.ok({ body }); + } + ); }; diff --git a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agents.ts b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agents.ts index 7f8f472ba578e..9b86261aa07b4 100644 --- a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agents.ts +++ b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agents.ts @@ -7,30 +7,38 @@ import { schema } from '@kbn/config-schema'; import type { IRouter } from '@kbn/core/server'; +import { API_VERSIONS } from '../../../common/constants'; import { PLUGIN_ID } from '../../../common'; import type { OsqueryAppContext } from '../../lib/osquery_app_context_services'; export const getAgentsRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { - router.get( - { + router.versioned + .get({ + access: 'internal', path: '/internal/osquery/fleet_wrapper/agents', - validate: { - query: schema.object({}, { unknowns: 'allow' }), - }, options: { tags: [`access:${PLUGIN_ID}-read`] }, - }, - async (context, request, response) => { - let agents; - try { - agents = await osqueryContext.service - .getAgentService() - ?.asInternalUser // @ts-expect-error update types - .listAgents(request.query); - } catch (error) { - return response.badRequest({ body: error }); - } + }) + .addVersion( + { + version: API_VERSIONS.internal.v1, + validate: { + request: { + query: schema.object({}, { unknowns: 'allow' }), + }, + }, + }, + async (context, request, response) => { + let agents; + try { + agents = await osqueryContext.service + .getAgentService() + ?.asInternalUser // @ts-expect-error update types + .listAgents(request.query); + } catch (error) { + return response.badRequest({ body: error }); + } - return response.ok({ body: agents }); - } - ); + return response.ok({ body: agents }); + } + ); }; diff --git a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_package_policies.ts b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_package_policies.ts index 2b9004ee3c882..cbf5414eb4f87 100644 --- a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_package_policies.ts +++ b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_package_policies.ts @@ -8,33 +8,41 @@ import { schema } from '@kbn/config-schema'; import type { IRouter } from '@kbn/core/server'; import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common'; +import { API_VERSIONS } from '../../../common/constants'; import { PLUGIN_ID, OSQUERY_INTEGRATION_NAME } from '../../../common'; import type { OsqueryAppContext } from '../../lib/osquery_app_context_services'; import { getInternalSavedObjectsClient } from '../utils'; export const getPackagePoliciesRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { - router.get( - { + router.versioned + .get({ + access: 'internal', path: '/internal/osquery/fleet_wrapper/package_policies', - validate: { - query: schema.object({}, { unknowns: 'allow' }), - }, options: { tags: [`access:${PLUGIN_ID}-read`] }, - }, - async (context, request, response) => { - const internalSavedObjectsClient = await getInternalSavedObjectsClient( - osqueryContext.getStartServices - ); - const kuery = `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.attributes.package.name: ${OSQUERY_INTEGRATION_NAME}`; - const packagePolicyService = osqueryContext.service.getPackagePolicyService(); - const policies = await packagePolicyService?.list(internalSavedObjectsClient, { - kuery, - perPage: 1000, - }); + }) + .addVersion( + { + version: API_VERSIONS.internal.v1, + validate: { + request: { + query: schema.object({}, { unknowns: 'allow' }), + }, + }, + }, + async (context, request, response) => { + const internalSavedObjectsClient = await getInternalSavedObjectsClient( + osqueryContext.getStartServices + ); + const kuery = `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.attributes.package.name: ${OSQUERY_INTEGRATION_NAME}`; + const packagePolicyService = osqueryContext.service.getPackagePolicyService(); + const policies = await packagePolicyService?.list(internalSavedObjectsClient, { + kuery, + perPage: 1000, + }); - return response.ok({ - body: policies, - }); - } - ); + return response.ok({ + body: policies, + }); + } + ); }; diff --git a/x-pack/plugins/osquery/server/routes/live_query/create_live_query_route.ts b/x-pack/plugins/osquery/server/routes/live_query/create_live_query_route.ts index d9bb8eb8efb25..971f2c3b45a4c 100644 --- a/x-pack/plugins/osquery/server/routes/live_query/create_live_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/live_query/create_live_query_route.ts @@ -11,6 +11,7 @@ import markdown from 'remark-parse-no-trim'; import { some, filter } from 'lodash'; import deepEqual from 'fast-deep-equal'; import type { ECSMappingOrUndefined } from '@kbn/osquery-io-ts-types'; +import { API_VERSIONS } from '../../../common/constants'; import { PARAMETER_NOT_FOUND } from '../../../common/translations/errors'; import { replaceParamsQuery } from '../../../common/utils/replace_params_query'; import { createLiveQueryRequestBodySchema } from '../../../common/schemas/routes/live_query'; @@ -21,107 +22,114 @@ import { createActionHandler } from '../../handlers'; import { parser as OsqueryParser } from './osquery_parser'; export const createLiveQueryRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { - router.post( - { + router.versioned + .post({ + access: 'public', path: '/api/osquery/live_queries', - validate: { - body: buildRouteValidation< - typeof createLiveQueryRequestBodySchema, - CreateLiveQueryRequestBodySchema - >(createLiveQueryRequestBodySchema), + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: { + body: buildRouteValidation< + typeof createLiveQueryRequestBodySchema, + CreateLiveQueryRequestBodySchema + >(createLiveQueryRequestBodySchema), + }, + }, }, - }, - async (context, request, response) => { - const [coreStartServices] = await osqueryContext.getStartServices(); - const soClient = (await context.core).savedObjects.client; + async (context, request, response) => { + const [coreStartServices] = await osqueryContext.getStartServices(); + const soClient = (await context.core).savedObjects.client; - const { - osquery: { writeLiveQueries, runSavedQueries }, - } = await coreStartServices.capabilities.resolveCapabilities(request); + const { + osquery: { writeLiveQueries, runSavedQueries }, + } = await coreStartServices.capabilities.resolveCapabilities(request); - const isInvalid = !( - writeLiveQueries || - (runSavedQueries && (request.body.saved_query_id || request.body.pack_id)) - ); + const isInvalid = !( + writeLiveQueries || + (runSavedQueries && (request.body.saved_query_id || request.body.pack_id)) + ); - const client = await osqueryContext.service - .getRuleRegistryService() - ?.getRacClientWithRequest(request); + const client = await osqueryContext.service + .getRuleRegistryService() + ?.getRacClientWithRequest(request); - const alertData = request.body.alert_ids?.length - ? await client?.get({ id: request.body.alert_ids[0] }) - : undefined; + const alertData = request.body.alert_ids?.length + ? await client?.get({ id: request.body.alert_ids[0] }) + : undefined; - if (isInvalid) { - if (request.body.alert_ids?.length) { - try { - if (alertData?.['kibana.alert.rule.note']) { - const parsedAlertInvestigationGuide = unified() - .use([[markdown, {}], OsqueryParser]) - .parse(alertData?.['kibana.alert.rule.note']); + if (isInvalid) { + if (request.body.alert_ids?.length) { + try { + if (alertData?.['kibana.alert.rule.note']) { + const parsedAlertInvestigationGuide = unified() + .use([[markdown, {}], OsqueryParser]) + .parse(alertData?.['kibana.alert.rule.note']); - const osqueryQueries = filter(parsedAlertInvestigationGuide?.children as object, [ - 'type', - 'osquery', - ]); + const osqueryQueries = filter(parsedAlertInvestigationGuide?.children as object, [ + 'type', + 'osquery', + ]); - const requestQueryExistsInTheInvestigationGuide = some( - osqueryQueries, - (payload: { - configuration: { query: string; ecs_mapping: ECSMappingOrUndefined }; - }) => { - const { result: replacedConfigurationQuery } = replaceParamsQuery( - payload.configuration.query, - alertData - ); + const requestQueryExistsInTheInvestigationGuide = some( + osqueryQueries, + (payload: { + configuration: { query: string; ecs_mapping: ECSMappingOrUndefined }; + }) => { + const { result: replacedConfigurationQuery } = replaceParamsQuery( + payload.configuration.query, + alertData + ); - return ( - replacedConfigurationQuery === request.body.query && - deepEqual(payload.configuration.ecs_mapping, request.body.ecs_mapping) - ); - } - ); + return ( + replacedConfigurationQuery === request.body.query && + deepEqual(payload.configuration.ecs_mapping, request.body.ecs_mapping) + ); + } + ); - if (!requestQueryExistsInTheInvestigationGuide) throw new Error(); + if (!requestQueryExistsInTheInvestigationGuide) throw new Error(); + } + } catch (error) { + return response.forbidden(); } - } catch (error) { + } else { return response.forbidden(); } - } else { - return response.forbidden(); } - } - try { - const currentUser = await osqueryContext.security.authc.getCurrentUser(request)?.username; - const { response: osqueryAction, fleetActionsCount } = await createActionHandler( - osqueryContext, - request.body, - { - soClient, - metadata: { currentUser }, - alertData, + try { + const currentUser = await osqueryContext.security.authc.getCurrentUser(request)?.username; + const { response: osqueryAction, fleetActionsCount } = await createActionHandler( + osqueryContext, + request.body, + { + soClient, + metadata: { currentUser }, + alertData, + } + ); + if (!fleetActionsCount) { + return response.badRequest({ + body: PARAMETER_NOT_FOUND, + }); } - ); - if (!fleetActionsCount) { - return response.badRequest({ - body: PARAMETER_NOT_FOUND, + + return response.ok({ + body: { data: osqueryAction }, }); - } + } catch (error) { + if (error.statusCode === 400) { + return response.badRequest({ body: error }); + } - return response.ok({ - body: { data: osqueryAction }, - }); - } catch (error) { - if (error.statusCode === 400) { - return response.badRequest({ body: error }); + return response.customError({ + statusCode: 500, + body: new Error(`Error occurred while processing ${error}`), + }); } - - return response.customError({ - statusCode: 500, - body: new Error(`Error occurred while processing ${error}`), - }); } - } - ); + ); }; diff --git a/x-pack/plugins/osquery/server/routes/live_query/find_live_query_route.ts b/x-pack/plugins/osquery/server/routes/live_query/find_live_query_route.ts index 5186de9ce67be..858b76315c8e0 100644 --- a/x-pack/plugins/osquery/server/routes/live_query/find_live_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/live_query/find_live_query_route.ts @@ -11,6 +11,7 @@ import { omit } from 'lodash'; import type { Observable } from 'rxjs'; import { lastValueFrom } from 'rxjs'; import type { DataRequestHandlerContext } from '@kbn/data-plugin/server'; +import { API_VERSIONS } from '../../../common/constants'; import { PLUGIN_ID } from '../../../common'; import type { @@ -22,64 +23,73 @@ import { OsqueryQueries } from '../../../common/search_strategy'; import { createFilter, generateTablePaginationOptions } from '../../../common/utils/build_query'; export const findLiveQueryRoute = (router: IRouter) => { - router.get( - { + router.versioned + .get({ + access: 'public', path: '/api/osquery/live_queries', - validate: { - query: schema.object( - { - filterQuery: schema.maybe(schema.string()), - page: schema.maybe(schema.number()), - pageSize: schema.maybe(schema.number()), - sort: schema.maybe(schema.string()), - sortOrder: schema.maybe(schema.oneOf([schema.literal('asc'), schema.literal('desc')])), + options: { tags: [`access:${PLUGIN_ID}-read`] }, + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: { + query: schema.object( + { + filterQuery: schema.maybe(schema.string()), + page: schema.maybe(schema.number()), + pageSize: schema.maybe(schema.number()), + sort: schema.maybe(schema.string()), + sortOrder: schema.maybe( + schema.oneOf([schema.literal('asc'), schema.literal('desc')]) + ), + }, + { unknowns: 'allow' } + ), }, - { unknowns: 'allow' } - ), + }, }, - options: { tags: [`access:${PLUGIN_ID}-read`] }, - }, - async (context, request, response) => { - const abortSignal = getRequestAbortedSignal(request.events.aborted$); + async (context, request, response) => { + const abortSignal = getRequestAbortedSignal(request.events.aborted$); - try { - const search = await context.search; - const res = await lastValueFrom( - search.search( - { - factoryQueryType: OsqueryQueries.actions, - filterQuery: createFilter(request.query.filterQuery), - pagination: generateTablePaginationOptions( - request.query.page ?? 0, - request.query.pageSize ?? 100 - ), - sort: { - direction: (request.query.sortOrder ?? 'desc') as Direction, - field: request.query.sort ?? 'created_at', + try { + const search = await context.search; + const res = await lastValueFrom( + search.search( + { + factoryQueryType: OsqueryQueries.actions, + filterQuery: createFilter(request.query.filterQuery), + pagination: generateTablePaginationOptions( + request.query.page ?? 0, + request.query.pageSize ?? 100 + ), + sort: { + direction: (request.query.sortOrder ?? 'desc') as Direction, + field: request.query.sort ?? 'created_at', + }, }, - }, - { abortSignal, strategy: 'osquerySearchStrategy' } - ) - ); + { abortSignal, strategy: 'osquerySearchStrategy' } + ) + ); - return response.ok({ - body: { - data: { - ...omit(res, 'edges'), - items: res.edges, + return response.ok({ + body: { + data: { + ...omit(res, 'edges'), + items: res.edges, + }, }, - }, - }); - } catch (e) { - return response.customError({ - statusCode: e.statusCode ?? 500, - body: { - message: e.message, - }, - }); + }); + } catch (e) { + return response.customError({ + statusCode: e.statusCode ?? 500, + body: { + message: e.message, + }, + }); + } } - } - ); + ); }; function getRequestAbortedSignal(aborted$: Observable): AbortSignal { diff --git a/x-pack/plugins/osquery/server/routes/live_query/get_live_query_details_route.ts b/x-pack/plugins/osquery/server/routes/live_query/get_live_query_details_route.ts index 71da29d04c416..6bd2bf3a11963 100644 --- a/x-pack/plugins/osquery/server/routes/live_query/get_live_query_details_route.ts +++ b/x-pack/plugins/osquery/server/routes/live_query/get_live_query_details_route.ts @@ -11,6 +11,7 @@ import { every, map, mapKeys, pick, reduce } from 'lodash'; import type { Observable } from 'rxjs'; import { lastValueFrom, zip } from 'rxjs'; import type { DataRequestHandlerContext } from '@kbn/data-plugin/server'; +import { API_VERSIONS } from '../../../common/constants'; import { PLUGIN_ID } from '../../../common'; import { getActionResponses } from './utils'; @@ -21,108 +22,115 @@ import type { import { OsqueryQueries } from '../../../common/search_strategy'; export const getLiveQueryDetailsRoute = (router: IRouter) => { - router.get( - { + router.versioned + .get({ + access: 'public', path: '/api/osquery/live_queries/{id}', - validate: { - params: schema.object( - { - id: schema.string(), + options: { tags: [`access:${PLUGIN_ID}-read`] }, + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: { + params: schema.object( + { + id: schema.string(), + }, + { unknowns: 'allow' } + ), + query: schema.object({}, { unknowns: 'allow' }), }, - { unknowns: 'allow' } - ), - query: schema.object({}, { unknowns: 'allow' }), + }, }, - options: { tags: [`access:${PLUGIN_ID}-read`] }, - }, - async (context, request, response) => { - const abortSignal = getRequestAbortedSignal(request.events.aborted$); + async (context, request, response) => { + const abortSignal = getRequestAbortedSignal(request.events.aborted$); - try { - const search = await context.search; - const { actionDetails } = await lastValueFrom( - search.search( - { - actionId: request.params.id, - filterQuery: request.query, - factoryQueryType: OsqueryQueries.actionDetails, - }, - { abortSignal, strategy: 'osquerySearchStrategy' } - ) - ); + try { + const search = await context.search; + const { actionDetails } = await lastValueFrom( + search.search( + { + actionId: request.params.id, + filterQuery: request.query, + factoryQueryType: OsqueryQueries.actionDetails, + }, + { abortSignal, strategy: 'osquerySearchStrategy' } + ) + ); - const queries = actionDetails?._source?.queries; - const expirationDate = actionDetails?.fields?.expiration[0]; + const queries = actionDetails?._source?.queries; + const expirationDate = actionDetails?.fields?.expiration[0]; - const expired = !expirationDate ? true : new Date(expirationDate) < new Date(); + const expired = !expirationDate ? true : new Date(expirationDate) < new Date(); - const responseData = await lastValueFrom( - zip( - ...map(queries, (query) => - getActionResponses(search, query.action_id, query.agents?.length ?? 0) + const responseData = await lastValueFrom( + zip( + ...map(queries, (query) => + getActionResponses(search, query.action_id, query.agents?.length ?? 0) + ) ) - ) - ); + ); - const isCompleted = expired || (responseData && every(responseData, ['pending', 0])); - const agentByActionIdStatusMap = mapKeys(responseData, 'action_id'); + const isCompleted = expired || (responseData && every(responseData, ['pending', 0])); + const agentByActionIdStatusMap = mapKeys(responseData, 'action_id'); - return response.ok({ - body: { - data: { - ...pick( - actionDetails._source, - 'action_id', - 'expiration', - '@timestamp', - 'agent_selection', - 'agents', - 'user_id', - 'pack_id', - 'pack_name', - 'prebuilt_pack' - ), - queries: reduce< - { - action_id: string; - id: string; - query: string; - agents: string[]; - ecs_mapping?: unknown; - version?: string; - platform?: string; - saved_query_id?: string; - }, - Array> - >( - actionDetails._source?.queries, - (acc, query) => { - const agentStatus = agentByActionIdStatusMap[query.action_id]; + return response.ok({ + body: { + data: { + ...pick( + actionDetails._source, + 'action_id', + 'expiration', + '@timestamp', + 'agent_selection', + 'agents', + 'user_id', + 'pack_id', + 'pack_name', + 'prebuilt_pack' + ), + queries: reduce< + { + action_id: string; + id: string; + query: string; + agents: string[]; + ecs_mapping?: unknown; + version?: string; + platform?: string; + saved_query_id?: string; + }, + Array> + >( + actionDetails._source?.queries, + (acc, query) => { + const agentStatus = agentByActionIdStatusMap[query.action_id]; - acc.push({ - ...query, - ...agentStatus, - status: isCompleted || agentStatus?.pending === 0 ? 'completed' : 'running', - }); + acc.push({ + ...query, + ...agentStatus, + status: isCompleted || agentStatus?.pending === 0 ? 'completed' : 'running', + }); - return acc; - }, - [] as Array> - ), - status: isCompleted ? 'completed' : 'running', + return acc; + }, + [] as Array> + ), + status: isCompleted ? 'completed' : 'running', + }, }, - }, - }); - } catch (e) { - return response.customError({ - statusCode: e.statusCode ?? 500, - body: { - message: e.message, - }, - }); + }); + } catch (e) { + return response.customError({ + statusCode: e.statusCode ?? 500, + body: { + message: e.message, + }, + }); + } } - } - ); + ); }; function getRequestAbortedSignal(aborted$: Observable): AbortSignal { diff --git a/x-pack/plugins/osquery/server/routes/live_query/get_live_query_results_route.ts b/x-pack/plugins/osquery/server/routes/live_query/get_live_query_results_route.ts index c47772bbc7039..c7a86d440e344 100644 --- a/x-pack/plugins/osquery/server/routes/live_query/get_live_query_results_route.ts +++ b/x-pack/plugins/osquery/server/routes/live_query/get_live_query_results_route.ts @@ -11,6 +11,7 @@ import { map } from 'lodash'; import type { Observable } from 'rxjs'; import { lastValueFrom, zip } from 'rxjs'; import type { DataRequestHandlerContext } from '@kbn/data-plugin/server'; +import { API_VERSIONS } from '../../../common/constants'; import { PLUGIN_ID } from '../../../common'; import type { ActionDetailsRequestOptions, @@ -21,88 +22,97 @@ import { createFilter, generateTablePaginationOptions } from '../../../common/ut import { getActionResponses } from './utils'; export const getLiveQueryResultsRoute = (router: IRouter) => { - router.get( - { + router.versioned + .get({ + access: 'public', path: '/api/osquery/live_queries/{id}/results/{actionId}', - validate: { - query: schema.object( - { - filterQuery: schema.maybe(schema.string()), - page: schema.maybe(schema.number()), - pageSize: schema.maybe(schema.number()), - sort: schema.maybe(schema.string()), - sortOrder: schema.maybe(schema.oneOf([schema.literal('asc'), schema.literal('desc')])), - }, - { unknowns: 'allow' } - ), - params: schema.object( - { - id: schema.string(), - actionId: schema.string(), + options: { tags: [`access:${PLUGIN_ID}-read`] }, + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: { + query: schema.object( + { + filterQuery: schema.maybe(schema.string()), + page: schema.maybe(schema.number()), + pageSize: schema.maybe(schema.number()), + sort: schema.maybe(schema.string()), + sortOrder: schema.maybe( + schema.oneOf([schema.literal('asc'), schema.literal('desc')]) + ), + }, + { unknowns: 'allow' } + ), + params: schema.object( + { + id: schema.string(), + actionId: schema.string(), + }, + { unknowns: 'allow' } + ), }, - { unknowns: 'allow' } - ), + }, }, - options: { tags: [`access:${PLUGIN_ID}-read`] }, - }, - async (context, request, response) => { - const abortSignal = getRequestAbortedSignal(request.events.aborted$); + async (context, request, response) => { + const abortSignal = getRequestAbortedSignal(request.events.aborted$); - try { - const search = await context.search; - const { actionDetails } = await lastValueFrom( - search.search( - { - actionId: request.params.id, - filterQuery: createFilter(request.query.filterQuery), - factoryQueryType: OsqueryQueries.actionDetails, - }, - { abortSignal, strategy: 'osquerySearchStrategy' } - ) - ); + try { + const search = await context.search; + const { actionDetails } = await lastValueFrom( + search.search( + { + actionId: request.params.id, + filterQuery: createFilter(request.query.filterQuery), + factoryQueryType: OsqueryQueries.actionDetails, + }, + { abortSignal, strategy: 'osquerySearchStrategy' } + ) + ); - const queries = actionDetails?._source?.queries; + const queries = actionDetails?._source?.queries; - await lastValueFrom( - zip( - ...map(queries, (query) => - getActionResponses(search, query.action_id, query.agents?.length ?? 0) + await lastValueFrom( + zip( + ...map(queries, (query) => + getActionResponses(search, query.action_id, query.agents?.length ?? 0) + ) ) - ) - ); + ); - const res = await lastValueFrom( - search.search<{}>( - { - actionId: request.params.actionId, - factoryQueryType: OsqueryQueries.results, - filterQuery: createFilter(request.query.filterQuery), - pagination: generateTablePaginationOptions( - request.query.page ?? 0, - request.query.pageSize ?? 100 - ), - sort: { - direction: request.query.sortOrder ?? 'desc', - field: request.query.sort ?? '@timestamp', + const res = await lastValueFrom( + search.search<{}>( + { + actionId: request.params.actionId, + factoryQueryType: OsqueryQueries.results, + filterQuery: createFilter(request.query.filterQuery), + pagination: generateTablePaginationOptions( + request.query.page ?? 0, + request.query.pageSize ?? 100 + ), + sort: { + direction: request.query.sortOrder ?? 'desc', + field: request.query.sort ?? '@timestamp', + }, }, - }, - { abortSignal, strategy: 'osquerySearchStrategy' } - ) - ); + { abortSignal, strategy: 'osquerySearchStrategy' } + ) + ); - return response.ok({ - body: { data: res }, - }); - } catch (e) { - return response.customError({ - statusCode: e.statusCode ?? 500, - body: { - message: e.message, - }, - }); + return response.ok({ + body: { data: res }, + }); + } catch (e) { + return response.customError({ + statusCode: e.statusCode ?? 500, + body: { + message: e.message, + }, + }); + } } - } - ); + ); }; function getRequestAbortedSignal(aborted$: Observable): AbortSignal { diff --git a/x-pack/plugins/osquery/server/routes/pack/create_pack_route.ts b/x-pack/plugins/osquery/server/routes/pack/create_pack_route.ts index f505d5edd87de..5d12a41f03c4d 100644 --- a/x-pack/plugins/osquery/server/routes/pack/create_pack_route.ts +++ b/x-pack/plugins/osquery/server/routes/pack/create_pack_route.ts @@ -16,6 +16,7 @@ import { PACKAGE_POLICY_SAVED_OBJECT_TYPE, } from '@kbn/fleet-plugin/common'; import type { IRouter } from '@kbn/core/server'; +import { API_VERSIONS } from '../../../common/constants'; import type { OsqueryAppContext } from '../../lib/osquery_app_context_services'; import { OSQUERY_INTEGRATION_NAME } from '../../../common'; import { PLUGIN_ID } from '../../../common'; @@ -33,170 +34,177 @@ import type { PackResponseData } from './types'; type PackSavedObjectLimited = Omit; export const createPackRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { - router.post( - { + router.versioned + .post({ + access: 'public', path: '/api/osquery/packs', - validate: { - body: schema.object( - { - name: schema.string(), - description: schema.maybe(schema.string()), - enabled: schema.maybe(schema.boolean()), - policy_ids: schema.maybe(schema.arrayOf(schema.string())), - shards: schema.recordOf(schema.string(), schema.number()), - queries: schema.recordOf( - schema.string(), - schema.object({ - query: schema.string(), - interval: schema.maybe(schema.number()), - snapshot: schema.maybe(schema.boolean()), - removed: schema.maybe(schema.boolean()), - platform: schema.maybe(schema.string()), - version: schema.maybe(schema.string()), - ecs_mapping: schema.maybe( - schema.recordOf( - schema.string(), - schema.object({ - field: schema.maybe(schema.string()), - value: schema.maybe( - schema.oneOf([schema.string(), schema.arrayOf(schema.string())]) - ), - }) - ) + options: { tags: [`access:${PLUGIN_ID}-writePacks`] }, + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: { + body: schema.object( + { + name: schema.string(), + description: schema.maybe(schema.string()), + enabled: schema.maybe(schema.boolean()), + policy_ids: schema.maybe(schema.arrayOf(schema.string())), + shards: schema.recordOf(schema.string(), schema.number()), + queries: schema.recordOf( + schema.string(), + schema.object({ + query: schema.string(), + interval: schema.maybe(schema.number()), + snapshot: schema.maybe(schema.boolean()), + removed: schema.maybe(schema.boolean()), + platform: schema.maybe(schema.string()), + version: schema.maybe(schema.string()), + ecs_mapping: schema.maybe( + schema.recordOf( + schema.string(), + schema.object({ + field: schema.maybe(schema.string()), + value: schema.maybe( + schema.oneOf([schema.string(), schema.arrayOf(schema.string())]) + ), + }) + ) + ), + }) ), - }) + }, + { unknowns: 'allow' } ), }, - { unknowns: 'allow' } - ), - }, - options: { tags: [`access:${PLUGIN_ID}-writePacks`] }, - }, - async (context, request, response) => { - const coreContext = await context.core; - const esClient = coreContext.elasticsearch.client.asCurrentUser; - const savedObjectsClient = coreContext.savedObjects.client; - const internalSavedObjectsClient = await getInternalSavedObjectsClient( - osqueryContext.getStartServices - ); - const agentPolicyService = osqueryContext.service.getAgentPolicyService(); - - const packagePolicyService = osqueryContext.service.getPackagePolicyService(); - const currentUser = await osqueryContext.security.authc.getCurrentUser(request)?.username; - - // eslint-disable-next-line @typescript-eslint/naming-convention - const { name, description, queries, enabled, policy_ids, shards } = request.body; - const conflictingEntries = await savedObjectsClient.find({ - type: packSavedObjectType, - filter: `${packSavedObjectType}.attributes.name: "${name}"`, - }); - - if ( - conflictingEntries.saved_objects.length && - some(conflictingEntries.saved_objects, ['attributes.name', name]) - ) { - return response.conflict({ body: `Pack with name "${name}" already exists.` }); - } - - const { items: packagePolicies } = (await packagePolicyService?.list( - internalSavedObjectsClient, - { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${OSQUERY_INTEGRATION_NAME}`, - perPage: 1000, - page: 1, - } - )) ?? { items: [] }; - - const policiesList = getInitialPolicies(packagePolicies, policy_ids, shards); - - const agentPolicies = await agentPolicyService?.getByIds( - internalSavedObjectsClient, - policiesList - ); - - const policyShards = findMatchingShards(agentPolicies, shards); - - const agentPoliciesIdMap = mapKeys(agentPolicies, 'id'); - - const references = policiesList.map((id) => ({ - id, - name: agentPoliciesIdMap[id]?.name, - type: AGENT_POLICY_SAVED_OBJECT_TYPE, - })); - - const packSO = await savedObjectsClient.create( - packSavedObjectType, - { - name, - description, - queries: convertPackQueriesToSO(queries), - enabled, - created_at: moment().toISOString(), - created_by: currentUser, - updated_at: moment().toISOString(), - updated_by: currentUser, - shards: convertShardsToArray(shards), }, - { - references, - refresh: 'wait_for', + }, + async (context, request, response) => { + const coreContext = await context.core; + const esClient = coreContext.elasticsearch.client.asCurrentUser; + const savedObjectsClient = coreContext.savedObjects.client; + const internalSavedObjectsClient = await getInternalSavedObjectsClient( + osqueryContext.getStartServices + ); + const agentPolicyService = osqueryContext.service.getAgentPolicyService(); + + const packagePolicyService = osqueryContext.service.getPackagePolicyService(); + const currentUser = await osqueryContext.security.authc.getCurrentUser(request)?.username; + + // eslint-disable-next-line @typescript-eslint/naming-convention + const { name, description, queries, enabled, policy_ids, shards } = request.body; + const conflictingEntries = await savedObjectsClient.find({ + type: packSavedObjectType, + filter: `${packSavedObjectType}.attributes.name: "${name}"`, + }); + + if ( + conflictingEntries.saved_objects.length && + some(conflictingEntries.saved_objects, ['attributes.name', name]) + ) { + return response.conflict({ body: `Pack with name "${name}" already exists.` }); } - ); - - if (enabled && policiesList.length) { - await Promise.all( - policiesList.map((agentPolicyId) => { - const packagePolicy = find(packagePolicies, ['policy_id', agentPolicyId]); - if (packagePolicy) { - return packagePolicyService?.update( - internalSavedObjectsClient, - esClient, - packagePolicy.id, - produce(packagePolicy, (draft) => { - unset(draft, 'id'); - if (!has(draft, 'inputs[0].streams')) { - set(draft, 'inputs[0].streams', []); - } - - set(draft, `inputs[0].config.osquery.value.packs.${packSO.attributes.name}`, { - shard: policyShards[packagePolicy.policy_id] - ? policyShards[packagePolicy.policy_id] - : 100, - queries: convertSOQueriesToPackConfig(queries), - }); - - return draft; - }) - ); - } - }) + + const { items: packagePolicies } = (await packagePolicyService?.list( + internalSavedObjectsClient, + { + kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${OSQUERY_INTEGRATION_NAME}`, + perPage: 1000, + page: 1, + } + )) ?? { items: [] }; + + const policiesList = getInitialPolicies(packagePolicies, policy_ids, shards); + + const agentPolicies = await agentPolicyService?.getByIds( + internalSavedObjectsClient, + policiesList ); - } - set(packSO, 'attributes.queries', queries); - - const { attributes } = packSO; - - const data: PackResponseData = { - name: attributes.name, - description: attributes.description, - queries: attributes.queries, - version: attributes.version, - enabled: attributes.enabled, - created_at: attributes.created_at, - created_by: attributes.created_by, - updated_at: attributes.updated_at, - updated_by: attributes.updated_by, - policy_ids: attributes.policy_ids, - shards: attributes.shards, - saved_object_id: packSO.id, - }; - - return response.ok({ - body: { - data, - }, - }); - } - ); + const policyShards = findMatchingShards(agentPolicies, shards); + + const agentPoliciesIdMap = mapKeys(agentPolicies, 'id'); + + const references = policiesList.map((id) => ({ + id, + name: agentPoliciesIdMap[id]?.name, + type: AGENT_POLICY_SAVED_OBJECT_TYPE, + })); + + const packSO = await savedObjectsClient.create( + packSavedObjectType, + { + name, + description, + queries: convertPackQueriesToSO(queries), + enabled, + created_at: moment().toISOString(), + created_by: currentUser, + updated_at: moment().toISOString(), + updated_by: currentUser, + shards: convertShardsToArray(shards), + }, + { + references, + refresh: 'wait_for', + } + ); + + if (enabled && policiesList.length) { + await Promise.all( + policiesList.map((agentPolicyId) => { + const packagePolicy = find(packagePolicies, ['policy_id', agentPolicyId]); + if (packagePolicy) { + return packagePolicyService?.update( + internalSavedObjectsClient, + esClient, + packagePolicy.id, + produce(packagePolicy, (draft) => { + unset(draft, 'id'); + if (!has(draft, 'inputs[0].streams')) { + set(draft, 'inputs[0].streams', []); + } + + set(draft, `inputs[0].config.osquery.value.packs.${packSO.attributes.name}`, { + shard: policyShards[packagePolicy.policy_id] + ? policyShards[packagePolicy.policy_id] + : 100, + queries: convertSOQueriesToPackConfig(queries), + }); + + return draft; + }) + ); + } + }) + ); + } + + set(packSO, 'attributes.queries', queries); + + const { attributes } = packSO; + + const data: PackResponseData = { + name: attributes.name, + description: attributes.description, + queries: attributes.queries, + version: attributes.version, + enabled: attributes.enabled, + created_at: attributes.created_at, + created_by: attributes.created_by, + updated_at: attributes.updated_at, + updated_by: attributes.updated_by, + policy_ids: attributes.policy_ids, + shards: attributes.shards, + saved_object_id: packSO.id, + }; + + return response.ok({ + body: { + data, + }, + }); + } + ); }; diff --git a/x-pack/plugins/osquery/server/routes/pack/delete_pack_route.ts b/x-pack/plugins/osquery/server/routes/pack/delete_pack_route.ts index cfcab5ad5b259..33a569653ea1d 100644 --- a/x-pack/plugins/osquery/server/routes/pack/delete_pack_route.ts +++ b/x-pack/plugins/osquery/server/routes/pack/delete_pack_route.ts @@ -10,6 +10,7 @@ import { produce } from 'immer'; import { schema } from '@kbn/config-schema'; import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common'; import type { IRouter } from '@kbn/core/server'; +import { API_VERSIONS } from '../../../common/constants'; import { OSQUERY_INTEGRATION_NAME } from '../../../common'; import { PLUGIN_ID } from '../../../common'; @@ -17,62 +18,72 @@ import { packSavedObjectType } from '../../../common/types'; import type { OsqueryAppContext } from '../../lib/osquery_app_context_services'; export const deletePackRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { - router.delete( - { + router.versioned + .delete({ + access: 'public', path: '/api/osquery/packs/{id}', - validate: { - params: schema.object({ - id: schema.string(), - }), - }, options: { tags: [`access:${PLUGIN_ID}-writePacks`] }, - }, - async (context, request, response) => { - const coreContext = await context.core; - const esClient = coreContext.elasticsearch.client.asCurrentUser; - const savedObjectsClient = coreContext.savedObjects.client; - const packagePolicyService = osqueryContext.service.getPackagePolicyService(); + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: { + params: schema.object({ + id: schema.string(), + }), + }, + }, + }, + async (context, request, response) => { + const coreContext = await context.core; + const esClient = coreContext.elasticsearch.client.asCurrentUser; + const savedObjectsClient = coreContext.savedObjects.client; + const packagePolicyService = osqueryContext.service.getPackagePolicyService(); - const currentPackSO = await savedObjectsClient.get<{ name: string }>( - packSavedObjectType, - request.params.id - ); + const currentPackSO = await savedObjectsClient.get<{ name: string }>( + packSavedObjectType, + request.params.id + ); - await savedObjectsClient.delete(packSavedObjectType, request.params.id, { - refresh: 'wait_for', - }); + await savedObjectsClient.delete(packSavedObjectType, request.params.id, { + refresh: 'wait_for', + }); - const { items: packagePolicies } = (await packagePolicyService?.list(savedObjectsClient, { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${OSQUERY_INTEGRATION_NAME}`, - perPage: 1000, - page: 1, - })) ?? { items: [] }; - const currentPackagePolicies = filter(packagePolicies, (packagePolicy) => - has(packagePolicy, `inputs[0].config.osquery.value.packs.${currentPackSO.attributes.name}`) - ); + const { items: packagePolicies } = (await packagePolicyService?.list(savedObjectsClient, { + kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${OSQUERY_INTEGRATION_NAME}`, + perPage: 1000, + page: 1, + })) ?? { items: [] }; + const currentPackagePolicies = filter(packagePolicies, (packagePolicy) => + has( + packagePolicy, + `inputs[0].config.osquery.value.packs.${currentPackSO.attributes.name}` + ) + ); - await Promise.all( - currentPackagePolicies.map((packagePolicy) => - packagePolicyService?.update( - savedObjectsClient, - esClient, - packagePolicy.id, - produce(packagePolicy, (draft) => { - unset(draft, 'id'); - unset( - draft, - `inputs[0].config.osquery.value.packs.${[currentPackSO.attributes.name]}` - ); + await Promise.all( + currentPackagePolicies.map((packagePolicy) => + packagePolicyService?.update( + savedObjectsClient, + esClient, + packagePolicy.id, + produce(packagePolicy, (draft) => { + unset(draft, 'id'); + unset( + draft, + `inputs[0].config.osquery.value.packs.${[currentPackSO.attributes.name]}` + ); - return draft; - }) + return draft; + }) + ) ) - ) - ); + ); - return response.ok({ - body: {}, - }); - } - ); + return response.ok({ + body: {}, + }); + } + ); }; diff --git a/x-pack/plugins/osquery/server/routes/pack/find_pack_route.ts b/x-pack/plugins/osquery/server/routes/pack/find_pack_route.ts index 0d40efc7c748c..394fc50ff4e29 100644 --- a/x-pack/plugins/osquery/server/routes/pack/find_pack_route.ts +++ b/x-pack/plugins/osquery/server/routes/pack/find_pack_route.ts @@ -10,69 +10,79 @@ import { schema } from '@kbn/config-schema'; import { AGENT_POLICY_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common'; import type { IRouter } from '@kbn/core/server'; +import { API_VERSIONS } from '../../../common/constants'; import { packSavedObjectType } from '../../../common/types'; import { PLUGIN_ID } from '../../../common'; import type { PackSavedObject } from '../../common/types'; import type { PackResponseData } from './types'; export const findPackRoute = (router: IRouter) => { - router.get( - { + router.versioned + .get({ + access: 'public', path: '/api/osquery/packs', - validate: { - query: schema.object( - { - page: schema.maybe(schema.number()), - pageSize: schema.maybe(schema.number()), - sort: schema.maybe(schema.string()), - sortOrder: schema.maybe(schema.oneOf([schema.literal('asc'), schema.literal('desc')])), + options: { tags: [`access:${PLUGIN_ID}-readPacks`] }, + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: { + query: schema.object( + { + page: schema.maybe(schema.number()), + pageSize: schema.maybe(schema.number()), + sort: schema.maybe(schema.string()), + sortOrder: schema.maybe( + schema.oneOf([schema.literal('asc'), schema.literal('desc')]) + ), + }, + { unknowns: 'allow' } + ), }, - { unknowns: 'allow' } - ), + }, }, - options: { tags: [`access:${PLUGIN_ID}-readPacks`] }, - }, - async (context, request, response) => { - const coreContext = await context.core; - const savedObjectsClient = coreContext.savedObjects.client; + async (context, request, response) => { + const coreContext = await context.core; + const savedObjectsClient = coreContext.savedObjects.client; - const soClientResponse = await savedObjectsClient.find({ - type: packSavedObjectType, - page: request.query.page ?? 1, - perPage: request.query.pageSize ?? 20, - sortField: request.query.sort ?? 'updated_at', - sortOrder: request.query.sortOrder ?? 'desc', - }); + const soClientResponse = await savedObjectsClient.find({ + type: packSavedObjectType, + page: request.query.page ?? 1, + perPage: request.query.pageSize ?? 20, + sortField: request.query.sort ?? 'updated_at', + sortOrder: request.query.sortOrder ?? 'desc', + }); - const packSavedObjects: PackResponseData[] = map(soClientResponse.saved_objects, (pack) => { - const policyIds = map( - filter(pack.references, ['type', AGENT_POLICY_SAVED_OBJECT_TYPE]), - 'id' - ); + const packSavedObjects: PackResponseData[] = map(soClientResponse.saved_objects, (pack) => { + const policyIds = map( + filter(pack.references, ['type', AGENT_POLICY_SAVED_OBJECT_TYPE]), + 'id' + ); - const { attributes } = pack; + const { attributes } = pack; - return { - name: attributes.name, - description: attributes.description, - queries: attributes.queries, - version: attributes.version, - enabled: attributes.enabled, - created_at: attributes.created_at, - created_by: attributes.created_by, - updated_at: attributes.updated_at, - updated_by: attributes.updated_by, - saved_object_id: pack.id, - policy_ids: policyIds, - }; - }); + return { + name: attributes.name, + description: attributes.description, + queries: attributes.queries, + version: attributes.version, + enabled: attributes.enabled, + created_at: attributes.created_at, + created_by: attributes.created_by, + updated_at: attributes.updated_at, + updated_by: attributes.updated_by, + saved_object_id: pack.id, + policy_ids: policyIds, + }; + }); - return response.ok({ - body: { - ...omit(soClientResponse, 'saved_objects'), - data: packSavedObjects, - }, - }); - } - ); + return response.ok({ + body: { + ...omit(soClientResponse, 'saved_objects'), + data: packSavedObjects, + }, + }); + } + ); }; diff --git a/x-pack/plugins/osquery/server/routes/pack/read_pack_route.ts b/x-pack/plugins/osquery/server/routes/pack/read_pack_route.ts index e0d2001758bd3..0a61b0310def0 100644 --- a/x-pack/plugins/osquery/server/routes/pack/read_pack_route.ts +++ b/x-pack/plugins/osquery/server/routes/pack/read_pack_route.ts @@ -9,6 +9,7 @@ import { filter, map } from 'lodash'; import { schema } from '@kbn/config-schema'; import { AGENT_POLICY_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common'; import type { IRouter } from '@kbn/core/server'; +import { API_VERSIONS } from '../../../common/constants'; import type { PackSavedObject } from '../../common/types'; import { PLUGIN_ID } from '../../../common'; @@ -18,54 +19,59 @@ import { convertShardsToObject } from '../utils'; import type { ReadPackResponseData } from './types'; export const readPackRoute = (router: IRouter) => { - router.get( - { + router.versioned + .get({ + access: 'public', path: '/api/osquery/packs/{id}', - validate: { - params: schema.object({ - id: schema.string(), - }), - }, options: { tags: [`access:${PLUGIN_ID}-readPacks`] }, - }, - async (context, request, response) => { - const coreContext = await context.core; - const savedObjectsClient = coreContext.savedObjects.client; + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: { + params: schema.object({ + id: schema.string(), + }), + }, + }, + }, + async (context, request, response) => { + const coreContext = await context.core; + const savedObjectsClient = coreContext.savedObjects.client; - const { attributes, references, id, ...rest } = await savedObjectsClient.get( - packSavedObjectType, - request.params.id - ); + const { attributes, references, id, ...rest } = + await savedObjectsClient.get(packSavedObjectType, request.params.id); - const policyIds = map(filter(references, ['type', AGENT_POLICY_SAVED_OBJECT_TYPE]), 'id'); - const osqueryPackAssetReference = !!filter(references, ['type', 'osquery-pack-asset']); + const policyIds = map(filter(references, ['type', AGENT_POLICY_SAVED_OBJECT_TYPE]), 'id'); + const osqueryPackAssetReference = !!filter(references, ['type', 'osquery-pack-asset']); - const data: ReadPackResponseData = { - type: rest.type, - namespaces: rest.namespaces, - migrationVersion: rest.migrationVersion, - managed: rest.managed, - coreMigrationVersion: rest.coreMigrationVersion, - name: attributes.name, - description: attributes.description, - version: attributes.version, - enabled: attributes.enabled, - created_at: attributes.created_at, - created_by: attributes.created_by, - updated_at: attributes.updated_at, - updated_by: attributes.updated_by, - saved_object_id: id, - queries: convertSOQueriesToPack(attributes.queries), - shards: convertShardsToObject(attributes.shards), - policy_ids: policyIds, - read_only: attributes.version !== undefined && osqueryPackAssetReference, - }; + const data: ReadPackResponseData = { + type: rest.type, + namespaces: rest.namespaces, + migrationVersion: rest.migrationVersion, + managed: rest.managed, + coreMigrationVersion: rest.coreMigrationVersion, + name: attributes.name, + description: attributes.description, + version: attributes.version, + enabled: attributes.enabled, + created_at: attributes.created_at, + created_by: attributes.created_by, + updated_at: attributes.updated_at, + updated_by: attributes.updated_by, + saved_object_id: id, + queries: convertSOQueriesToPack(attributes.queries), + shards: convertShardsToObject(attributes.shards), + policy_ids: policyIds, + read_only: attributes.version !== undefined && osqueryPackAssetReference, + }; - return response.ok({ - body: { - data, - }, - }); - } - ); + return response.ok({ + body: { + data, + }, + }); + } + ); }; diff --git a/x-pack/plugins/osquery/server/routes/pack/update_pack_route.ts b/x-pack/plugins/osquery/server/routes/pack/update_pack_route.ts index 7f928f3acf40f..0d05a4d1b30cb 100644 --- a/x-pack/plugins/osquery/server/routes/pack/update_pack_route.ts +++ b/x-pack/plugins/osquery/server/routes/pack/update_pack_route.ts @@ -17,6 +17,7 @@ import { } from '@kbn/fleet-plugin/common'; import type { IRouter } from '@kbn/core/server'; +import { API_VERSIONS } from '../../../common/constants'; import { OSQUERY_INTEGRATION_NAME } from '../../../common'; import { packSavedObjectType } from '../../../common/types'; import type { OsqueryAppContext } from '../../lib/osquery_app_context_services'; @@ -34,172 +35,264 @@ import type { PackSavedObject } from '../../common/types'; import type { PackResponseData } from './types'; export const updatePackRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { - router.put( - { + router.versioned + .put({ + access: 'public', path: '/api/osquery/packs/{id}', - validate: { - params: schema.object( - { - id: schema.string(), - }, - { unknowns: 'allow' } - ), - body: schema.object( - { - name: schema.maybe(schema.string()), - description: schema.maybe(schema.string()), - enabled: schema.maybe(schema.boolean()), - policy_ids: schema.maybe(schema.arrayOf(schema.string())), - shards: schema.maybe(schema.recordOf(schema.string(), schema.number())), - queries: schema.maybe( - schema.recordOf( - schema.string(), - schema.object({ - query: schema.string(), - interval: schema.maybe(schema.number()), - snapshot: schema.maybe(schema.boolean()), - removed: schema.maybe(schema.boolean()), - platform: schema.maybe(schema.string()), - version: schema.maybe(schema.string()), - ecs_mapping: schema.maybe( - schema.recordOf( - schema.string(), - schema.object({ - field: schema.maybe(schema.string()), - value: schema.maybe( - schema.oneOf([schema.string(), schema.arrayOf(schema.string())]) - ), - }) - ) - ), - }) - ) + options: { tags: [`access:${PLUGIN_ID}-writePacks`] }, + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: { + params: schema.object( + { + id: schema.string(), + }, + { unknowns: 'allow' } + ), + body: schema.object( + { + name: schema.maybe(schema.string()), + description: schema.maybe(schema.string()), + enabled: schema.maybe(schema.boolean()), + policy_ids: schema.maybe(schema.arrayOf(schema.string())), + shards: schema.maybe(schema.recordOf(schema.string(), schema.number())), + queries: schema.maybe( + schema.recordOf( + schema.string(), + schema.object({ + query: schema.string(), + interval: schema.maybe(schema.number()), + snapshot: schema.maybe(schema.boolean()), + removed: schema.maybe(schema.boolean()), + platform: schema.maybe(schema.string()), + version: schema.maybe(schema.string()), + ecs_mapping: schema.maybe( + schema.recordOf( + schema.string(), + schema.object({ + field: schema.maybe(schema.string()), + value: schema.maybe( + schema.oneOf([schema.string(), schema.arrayOf(schema.string())]) + ), + }) + ) + ), + }) + ) + ), + }, + { unknowns: 'allow' } ), }, - { unknowns: 'allow' } - ), + }, }, - options: { tags: [`access:${PLUGIN_ID}-writePacks`] }, - }, - async (context, request, response) => { - const coreContext = await context.core; - const esClient = coreContext.elasticsearch.client.asCurrentUser; - const savedObjectsClient = coreContext.savedObjects.client; - const internalSavedObjectsClient = await getInternalSavedObjectsClient( - osqueryContext.getStartServices - ); - const agentPolicyService = osqueryContext.service.getAgentPolicyService(); - const packagePolicyService = osqueryContext.service.getPackagePolicyService(); - const currentUser = await osqueryContext.security.authc.getCurrentUser(request)?.username; - - // eslint-disable-next-line @typescript-eslint/naming-convention - const { name, description, queries, enabled, policy_ids, shards = {} } = request.body; - - const currentPackSO = await savedObjectsClient.get<{ name: string; enabled: boolean }>( - packSavedObjectType, - request.params.id - ); - - if (name) { - const conflictingEntries = await savedObjectsClient.find({ - type: packSavedObjectType, - filter: `${packSavedObjectType}.attributes.name: "${name}"`, - }); + async (context, request, response) => { + const coreContext = await context.core; + const esClient = coreContext.elasticsearch.client.asCurrentUser; + const savedObjectsClient = coreContext.savedObjects.client; + const internalSavedObjectsClient = await getInternalSavedObjectsClient( + osqueryContext.getStartServices + ); + const agentPolicyService = osqueryContext.service.getAgentPolicyService(); + const packagePolicyService = osqueryContext.service.getPackagePolicyService(); + const currentUser = await osqueryContext.security.authc.getCurrentUser(request)?.username; - if ( - some( - filter(conflictingEntries.saved_objects, (packSO) => packSO.id !== currentPackSO.id), - ['attributes.name', name] - ) - ) { - return response.conflict({ body: `Pack with name "${name}" already exists.` }); - } - } + // eslint-disable-next-line @typescript-eslint/naming-convention + const { name, description, queries, enabled, policy_ids, shards = {} } = request.body; - const { items: packagePolicies } = (await packagePolicyService?.list( - internalSavedObjectsClient, - { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${OSQUERY_INTEGRATION_NAME}`, - perPage: 1000, - page: 1, + const currentPackSO = await savedObjectsClient.get<{ name: string; enabled: boolean }>( + packSavedObjectType, + request.params.id + ); + + if (name) { + const conflictingEntries = await savedObjectsClient.find({ + type: packSavedObjectType, + filter: `${packSavedObjectType}.attributes.name: "${name}"`, + }); + + if ( + some( + filter(conflictingEntries.saved_objects, (packSO) => packSO.id !== currentPackSO.id), + ['attributes.name', name] + ) + ) { + return response.conflict({ body: `Pack with name "${name}" already exists.` }); + } } - )) ?? { items: [] }; - const currentPackagePolicies = filter(packagePolicies, (packagePolicy) => - has(packagePolicy, `inputs[0].config.osquery.value.packs.${currentPackSO.attributes.name}`) - ); - const policiesList = getInitialPolicies(packagePolicies, policy_ids, shards); + const { items: packagePolicies } = (await packagePolicyService?.list( + internalSavedObjectsClient, + { + kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${OSQUERY_INTEGRATION_NAME}`, + perPage: 1000, + page: 1, + } + )) ?? { items: [] }; + const currentPackagePolicies = filter(packagePolicies, (packagePolicy) => + has( + packagePolicy, + `inputs[0].config.osquery.value.packs.${currentPackSO.attributes.name}` + ) + ); + + const policiesList = getInitialPolicies(packagePolicies, policy_ids, shards); - const agentPolicies = await agentPolicyService?.getByIds( - internalSavedObjectsClient, - policiesList - ); + const agentPolicies = await agentPolicyService?.getByIds( + internalSavedObjectsClient, + policiesList + ); - const policyShards = findMatchingShards(agentPolicies, shards); + const policyShards = findMatchingShards(agentPolicies, shards); - const agentPoliciesIdMap = mapKeys(agentPolicies, 'id'); + const agentPoliciesIdMap = mapKeys(agentPolicies, 'id'); - const nonAgentPolicyReferences = filter( - currentPackSO.references, - (reference) => reference.type !== AGENT_POLICY_SAVED_OBJECT_TYPE - ); - const getUpdatedReferences = () => { - if (!policy_ids && isEmpty(shards)) { - return currentPackSO.references; - } + const nonAgentPolicyReferences = filter( + currentPackSO.references, + (reference) => reference.type !== AGENT_POLICY_SAVED_OBJECT_TYPE + ); + const getUpdatedReferences = () => { + if (!policy_ids && isEmpty(shards)) { + return currentPackSO.references; + } + + return [ + ...nonAgentPolicyReferences, + ...policiesList.map((id) => ({ + id, + name: agentPoliciesIdMap[id]?.name, + type: AGENT_POLICY_SAVED_OBJECT_TYPE, + })), + ]; + }; + + const references = getUpdatedReferences(); + + await savedObjectsClient.update( + packSavedObjectType, + request.params.id, + { + enabled, + name, + description: description || '', + queries: queries && convertPackQueriesToSO(queries), + updated_at: moment().toISOString(), + updated_by: currentUser, + shards: convertShardsToArray(shards), + }, + { + refresh: 'wait_for', + references, + } + ); - return [ - ...nonAgentPolicyReferences, - ...policiesList.map((id) => ({ - id, - name: agentPoliciesIdMap[id]?.name, - type: AGENT_POLICY_SAVED_OBJECT_TYPE, - })), - ]; - }; - - const references = getUpdatedReferences(); - - await savedObjectsClient.update( - packSavedObjectType, - request.params.id, - { - enabled, - name, - description: description || '', - queries: queries && convertPackQueriesToSO(queries), - updated_at: moment().toISOString(), - updated_by: currentUser, - shards: convertShardsToArray(shards), - }, - { - refresh: 'wait_for', - references, + const currentAgentPolicyIds = map( + filter(currentPackSO.references, ['type', AGENT_POLICY_SAVED_OBJECT_TYPE]), + 'id' + ); + const updatedPackSO = await savedObjectsClient.get( + packSavedObjectType, + request.params.id + ); + + // @ts-expect-error update types + updatedPackSO.attributes.queries = convertSOQueriesToPack(updatedPackSO.attributes.queries); + + if (enabled == null && !currentPackSO.attributes.enabled) { + return response.ok({ body: { data: updatedPackSO } }); } - ); - - const currentAgentPolicyIds = map( - filter(currentPackSO.references, ['type', AGENT_POLICY_SAVED_OBJECT_TYPE]), - 'id' - ); - const updatedPackSO = await savedObjectsClient.get( - packSavedObjectType, - request.params.id - ); - - // @ts-expect-error update types - updatedPackSO.attributes.queries = convertSOQueriesToPack(updatedPackSO.attributes.queries); - - if (enabled == null && !currentPackSO.attributes.enabled) { - return response.ok({ body: { data: updatedPackSO } }); - } - if (enabled != null && enabled !== currentPackSO.attributes.enabled) { - if (enabled) { - const policyIds = policy_ids || !isEmpty(shards) ? policiesList : currentAgentPolicyIds; + if (enabled != null && enabled !== currentPackSO.attributes.enabled) { + if (enabled) { + const policyIds = policy_ids || !isEmpty(shards) ? policiesList : currentAgentPolicyIds; + + await Promise.all( + policyIds.map((agentPolicyId) => { + const packagePolicy = find(packagePolicies, ['policy_id', agentPolicyId]); + + if (packagePolicy) { + return packagePolicyService?.update( + internalSavedObjectsClient, + esClient, + packagePolicy.id, + produce(packagePolicy, (draft) => { + unset(draft, 'id'); + if (!has(draft, 'inputs[0].streams')) { + set(draft, 'inputs[0].streams', []); + } + + set( + draft, + `inputs[0].config.osquery.value.packs.${updatedPackSO.attributes.name}`, + { + queries: updatedPackSO.attributes.queries, + } + ); + + return draft; + }) + ); + } + }) + ); + } else { + await Promise.all( + currentAgentPolicyIds.map((agentPolicyId) => { + const packagePolicy = find(currentPackagePolicies, ['policy_id', agentPolicyId]); + if (!packagePolicy) return; + + return packagePolicyService?.update( + internalSavedObjectsClient, + esClient, + packagePolicy.id, + produce(packagePolicy, (draft) => { + unset(draft, 'id'); + unset( + draft, + `inputs[0].config.osquery.value.packs.${currentPackSO.attributes.name}` + ); + + return draft; + }) + ); + }) + ); + } + } else { + // TODO double check if policiesList shouldnt be changed into policyIds + const agentPolicyIdsToRemove = uniq(difference(currentAgentPolicyIds, policiesList)); + const agentPolicyIdsToUpdate = uniq( + difference(currentAgentPolicyIds, agentPolicyIdsToRemove) + ); + const agentPolicyIdsToAdd = uniq(difference(policiesList, currentAgentPolicyIds)); await Promise.all( - policyIds.map((agentPolicyId) => { + agentPolicyIdsToRemove.map((agentPolicyId) => { + const packagePolicy = find(currentPackagePolicies, ['policy_id', agentPolicyId]); + if (packagePolicy) { + return packagePolicyService?.update( + internalSavedObjectsClient, + esClient, + packagePolicy.id, + produce(packagePolicy, (draft) => { + unset(draft, 'id'); + unset( + draft, + `inputs[0].config.osquery.value.packs.${currentPackSO.attributes.name}` + ); + + return draft; + }) + ); + } + }) + ); + + await Promise.all( + agentPolicyIdsToUpdate.map((agentPolicyId) => { const packagePolicy = find(packagePolicies, ['policy_id', agentPolicyId]); if (packagePolicy) { @@ -209,15 +302,21 @@ export const updatePackRoute = (router: IRouter, osqueryContext: OsqueryAppConte packagePolicy.id, produce(packagePolicy, (draft) => { unset(draft, 'id'); - if (!has(draft, 'inputs[0].streams')) { - set(draft, 'inputs[0].streams', []); + if (updatedPackSO.attributes.name !== currentPackSO.attributes.name) { + unset( + draft, + `inputs[0].config.osquery.value.packs.${currentPackSO.attributes.name}` + ); } set( draft, `inputs[0].config.osquery.value.packs.${updatedPackSO.attributes.name}`, { - queries: updatedPackSO.attributes.queries, + shard: policyShards[packagePolicy.policy_id] + ? policyShards[packagePolicy.policy_id] + : 100, + queries: convertSOQueriesToPackConfig(updatedPackSO.attributes.queries), } ); @@ -227,149 +326,61 @@ export const updatePackRoute = (router: IRouter, osqueryContext: OsqueryAppConte } }) ); - } else { - await Promise.all( - currentAgentPolicyIds.map((agentPolicyId) => { - const packagePolicy = find(currentPackagePolicies, ['policy_id', agentPolicyId]); - if (!packagePolicy) return; - - return packagePolicyService?.update( - internalSavedObjectsClient, - esClient, - packagePolicy.id, - produce(packagePolicy, (draft) => { - unset(draft, 'id'); - unset( - draft, - `inputs[0].config.osquery.value.packs.${currentPackSO.attributes.name}` - ); - return draft; - }) - ); - }) - ); - } - } else { - // TODO double check if policiesList shouldnt be changed into policyIds - const agentPolicyIdsToRemove = uniq(difference(currentAgentPolicyIds, policiesList)); - const agentPolicyIdsToUpdate = uniq( - difference(currentAgentPolicyIds, agentPolicyIdsToRemove) - ); - const agentPolicyIdsToAdd = uniq(difference(policiesList, currentAgentPolicyIds)); - - await Promise.all( - agentPolicyIdsToRemove.map((agentPolicyId) => { - const packagePolicy = find(currentPackagePolicies, ['policy_id', agentPolicyId]); - if (packagePolicy) { - return packagePolicyService?.update( - internalSavedObjectsClient, - esClient, - packagePolicy.id, - produce(packagePolicy, (draft) => { - unset(draft, 'id'); - unset( - draft, - `inputs[0].config.osquery.value.packs.${currentPackSO.attributes.name}` - ); + await Promise.all( + agentPolicyIdsToAdd.map((agentPolicyId) => { + const packagePolicy = find(packagePolicies, ['policy_id', agentPolicyId]); - return draft; - }) - ); - } - }) - ); + if (packagePolicy) { + return packagePolicyService?.update( + internalSavedObjectsClient, + esClient, + packagePolicy.id, + produce(packagePolicy, (draft) => { + unset(draft, 'id'); + if (!(draft.inputs.length && draft.inputs[0].streams.length)) { + set(draft, 'inputs[0].streams', []); + } - await Promise.all( - agentPolicyIdsToUpdate.map((agentPolicyId) => { - const packagePolicy = find(packagePolicies, ['policy_id', agentPolicyId]); - - if (packagePolicy) { - return packagePolicyService?.update( - internalSavedObjectsClient, - esClient, - packagePolicy.id, - produce(packagePolicy, (draft) => { - unset(draft, 'id'); - if (updatedPackSO.attributes.name !== currentPackSO.attributes.name) { - unset( + set( draft, - `inputs[0].config.osquery.value.packs.${currentPackSO.attributes.name}` + `inputs[0].config.osquery.value.packs.${updatedPackSO.attributes.name}`, + { + shard: policyShards[packagePolicy.policy_id] + ? policyShards[packagePolicy.policy_id] + : 100, + queries: convertSOQueriesToPackConfig(updatedPackSO.attributes.queries), + } ); - } - - set( - draft, - `inputs[0].config.osquery.value.packs.${updatedPackSO.attributes.name}`, - { - shard: policyShards[packagePolicy.policy_id] - ? policyShards[packagePolicy.policy_id] - : 100, - queries: convertSOQueriesToPackConfig(updatedPackSO.attributes.queries), - } - ); - return draft; - }) - ); - } - }) - ); - - await Promise.all( - agentPolicyIdsToAdd.map((agentPolicyId) => { - const packagePolicy = find(packagePolicies, ['policy_id', agentPolicyId]); - - if (packagePolicy) { - return packagePolicyService?.update( - internalSavedObjectsClient, - esClient, - packagePolicy.id, - produce(packagePolicy, (draft) => { - unset(draft, 'id'); - if (!(draft.inputs.length && draft.inputs[0].streams.length)) { - set(draft, 'inputs[0].streams', []); - } - - set( - draft, - `inputs[0].config.osquery.value.packs.${updatedPackSO.attributes.name}`, - { - shard: policyShards[packagePolicy.policy_id] - ? policyShards[packagePolicy.policy_id] - : 100, - queries: convertSOQueriesToPackConfig(updatedPackSO.attributes.queries), - } - ); + return draft; + }) + ); + } + }) + ); + } - return draft; - }) - ); - } - }) - ); + const { attributes } = updatedPackSO; + + const data: PackResponseData = { + name: attributes.name, + description: attributes.description, + queries: attributes.queries, + version: attributes.version, + enabled: attributes.enabled, + created_at: attributes.created_at, + created_by: attributes.created_by, + updated_at: attributes.updated_at, + updated_by: attributes.updated_by, + policy_ids: attributes.policy_ids, + shards: attributes.shards, + saved_object_id: updatedPackSO.id, + }; + + return response.ok({ + body: { data }, + }); } - - const { attributes } = updatedPackSO; - - const data: PackResponseData = { - name: attributes.name, - description: attributes.description, - queries: attributes.queries, - version: attributes.version, - enabled: attributes.enabled, - created_at: attributes.created_at, - created_by: attributes.created_by, - updated_at: attributes.updated_at, - updated_by: attributes.updated_by, - policy_ids: attributes.policy_ids, - shards: attributes.shards, - saved_object_id: updatedPackSO.id, - }; - - return response.ok({ - body: { data }, - }); - } - ); + ); }; diff --git a/x-pack/plugins/osquery/server/routes/privileges_check/privileges_check_route.ts b/x-pack/plugins/osquery/server/routes/privileges_check/privileges_check_route.ts index 07e5e40ac69fd..b31da0c0e24da 100644 --- a/x-pack/plugins/osquery/server/routes/privileges_check/privileges_check_route.ts +++ b/x-pack/plugins/osquery/server/routes/privileges_check/privileges_check_route.ts @@ -6,39 +6,45 @@ */ import type { IRouter } from '@kbn/core/server'; +import { API_VERSIONS } from '../../../common/constants'; import { OSQUERY_INTEGRATION_NAME, PLUGIN_ID } from '../../../common'; import type { OsqueryAppContext } from '../../lib/osquery_app_context_services'; export const privilegesCheckRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { - router.get( - { + router.versioned + .get({ + access: 'internal', path: '/internal/osquery/privileges_check', - validate: {}, options: { tags: [`access:${PLUGIN_ID}-readLiveQueries`], }, - }, - async (context, request, response) => { - if (osqueryContext.security.authz.mode.useRbacForRequest(request)) { - const checkPrivileges = - osqueryContext.security.authz.checkPrivilegesDynamicallyWithRequest(request); - const { hasAllRequested } = await checkPrivileges({ - elasticsearch: { - cluster: [], - index: { - [`logs-${OSQUERY_INTEGRATION_NAME}.result*`]: ['read'], + }) + .addVersion( + { + version: API_VERSIONS.internal.v1, + validate: {}, + }, + async (context, request, response) => { + if (osqueryContext.security.authz.mode.useRbacForRequest(request)) { + const checkPrivileges = + osqueryContext.security.authz.checkPrivilegesDynamicallyWithRequest(request); + const { hasAllRequested } = await checkPrivileges({ + elasticsearch: { + cluster: [], + index: { + [`logs-${OSQUERY_INTEGRATION_NAME}.result*`]: ['read'], + }, }, - }, - }); + }); + + return response.ok({ + body: `${hasAllRequested}`, + }); + } return response.ok({ - body: `${hasAllRequested}`, + body: 'true', }); } - - return response.ok({ - body: 'true', - }); - } - ); + ); }; diff --git a/x-pack/plugins/osquery/server/routes/saved_query/create_saved_query_route.ts b/x-pack/plugins/osquery/server/routes/saved_query/create_saved_query_route.ts index e7c9c2a462ff8..8f40dbc28924a 100644 --- a/x-pack/plugins/osquery/server/routes/saved_query/create_saved_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/saved_query/create_saved_query_route.ts @@ -7,6 +7,7 @@ import { isEmpty, pickBy, some, isBoolean } from 'lodash'; import type { IRouter } from '@kbn/core/server'; +import { API_VERSIONS } from '../../../common/constants'; import type { SavedQueryResponse } from './types'; import type { SavedQuerySavedObject } from '../../common/types'; import { PLUGIN_ID } from '../../../common'; @@ -18,97 +19,104 @@ import type { OsqueryAppContext } from '../../lib/osquery_app_context_services'; import { convertECSMappingToArray } from '../utils'; export const createSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { - router.post( - { + router.versioned + .post({ + access: 'public', path: '/api/osquery/saved_queries', - validate: { - body: buildRouteValidation< - typeof createSavedQueryRequestSchema, - CreateSavedQueryRequestSchemaDecoded - >(createSavedQueryRequestSchema), - }, options: { tags: [`access:${PLUGIN_ID}-writeSavedQueries`] }, - }, - async (context, request, response) => { - const coreContext = await context.core; - const savedObjectsClient = coreContext.savedObjects.client; + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: { + body: buildRouteValidation< + typeof createSavedQueryRequestSchema, + CreateSavedQueryRequestSchemaDecoded + >(createSavedQueryRequestSchema), + }, + }, + }, + async (context, request, response) => { + const coreContext = await context.core; + const savedObjectsClient = coreContext.savedObjects.client; - const { - id, - description, - platform, - query, - version, - interval, - snapshot, - removed, - // eslint-disable-next-line @typescript-eslint/naming-convention - ecs_mapping, - } = request.body; + const { + id, + description, + platform, + query, + version, + interval, + snapshot, + removed, + // eslint-disable-next-line @typescript-eslint/naming-convention + ecs_mapping, + } = request.body; - const currentUser = await osqueryContext.security.authc.getCurrentUser(request)?.username; + const currentUser = await osqueryContext.security.authc.getCurrentUser(request)?.username; - const conflictingEntries = await savedObjectsClient.find({ - type: savedQuerySavedObjectType, - filter: `${savedQuerySavedObjectType}.attributes.id: "${id}"`, - }); + const conflictingEntries = await savedObjectsClient.find({ + type: savedQuerySavedObjectType, + filter: `${savedQuerySavedObjectType}.attributes.id: "${id}"`, + }); - if ( - conflictingEntries.saved_objects.length && - some(conflictingEntries.saved_objects, ['attributes.id', id]) - ) { - return response.conflict({ body: `Saved query with id "${id}" already exists.` }); - } + if ( + conflictingEntries.saved_objects.length && + some(conflictingEntries.saved_objects, ['attributes.id', id]) + ) { + return response.conflict({ body: `Saved query with id "${id}" already exists.` }); + } - const savedQuerySO = await savedObjectsClient.create( - savedQuerySavedObjectType, - pickBy( - { - id, - description, - query, - platform, - version, - interval, - snapshot, - removed, - ecs_mapping: convertECSMappingToArray(ecs_mapping), - created_by: currentUser, - created_at: new Date().toISOString(), - updated_by: currentUser, - updated_at: new Date().toISOString(), - }, - (value) => !isEmpty(value) || isBoolean(value) - ) - ); + const savedQuerySO = await savedObjectsClient.create( + savedQuerySavedObjectType, + pickBy( + { + id, + description, + query, + platform, + version, + interval, + snapshot, + removed, + ecs_mapping: convertECSMappingToArray(ecs_mapping), + created_by: currentUser, + created_at: new Date().toISOString(), + updated_by: currentUser, + updated_at: new Date().toISOString(), + }, + (value) => !isEmpty(value) || isBoolean(value) + ) + ); - const { attributes } = savedQuerySO; + const { attributes } = savedQuerySO; - const data: Partial = pickBy( - { - created_at: attributes.created_at, - created_by: attributes.created_by, - description: attributes.description, - id: attributes.id, - removed: attributes.removed, - snapshot: attributes.snapshot, - version: attributes.version, - interval: attributes.interval, - platform: attributes.platform, - query: attributes.query, - updated_at: attributes.updated_at, - updated_by: attributes.updated_by, - saved_object_id: savedQuerySO.id, - ecs_mapping, - }, - (value) => !isEmpty(value) - ); + const data: Partial = pickBy( + { + created_at: attributes.created_at, + created_by: attributes.created_by, + description: attributes.description, + id: attributes.id, + removed: attributes.removed, + snapshot: attributes.snapshot, + version: attributes.version, + interval: attributes.interval, + platform: attributes.platform, + query: attributes.query, + updated_at: attributes.updated_at, + updated_by: attributes.updated_by, + saved_object_id: savedQuerySO.id, + ecs_mapping, + }, + (value) => !isEmpty(value) + ); - return response.ok({ - body: { - data, - }, - }); - } - ); + return response.ok({ + body: { + data, + }, + }); + } + ); }; diff --git a/x-pack/plugins/osquery/server/routes/saved_query/delete_saved_query_route.ts b/x-pack/plugins/osquery/server/routes/saved_query/delete_saved_query_route.ts index ff025ede421f0..ccecbf7a66dd9 100644 --- a/x-pack/plugins/osquery/server/routes/saved_query/delete_saved_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/saved_query/delete_saved_query_route.ts @@ -7,41 +7,49 @@ import { schema } from '@kbn/config-schema'; import type { IRouter } from '@kbn/core/server'; +import { API_VERSIONS } from '../../../common/constants'; import { PLUGIN_ID } from '../../../common'; import { savedQuerySavedObjectType } from '../../../common/types'; import type { OsqueryAppContext } from '../../lib/osquery_app_context_services'; import { isSavedQueryPrebuilt } from './utils'; export const deleteSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { - router.delete( - { + router.versioned + .delete({ + access: 'public', path: '/api/osquery/saved_queries/{id}', - validate: { - params: schema.object({ - id: schema.string(), - }), - }, options: { tags: [`access:${PLUGIN_ID}-writeSavedQueries`] }, - }, - async (context, request, response) => { - const coreContext = await context.core; - const savedObjectsClient = coreContext.savedObjects.client; + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: { + params: schema.object({ + id: schema.string(), + }), + }, + }, + }, + async (context, request, response) => { + const coreContext = await context.core; + const savedObjectsClient = coreContext.savedObjects.client; - const isPrebuilt = await isSavedQueryPrebuilt( - osqueryContext.service.getPackageService()?.asInternalUser, - request.params.id - ); - if (isPrebuilt) { - return response.conflict({ body: `Elastic prebuilt Saved query cannot be deleted.` }); - } + const isPrebuilt = await isSavedQueryPrebuilt( + osqueryContext.service.getPackageService()?.asInternalUser, + request.params.id + ); + if (isPrebuilt) { + return response.conflict({ body: `Elastic prebuilt Saved query cannot be deleted.` }); + } - await savedObjectsClient.delete(savedQuerySavedObjectType, request.params.id, { - refresh: 'wait_for', - }); + await savedObjectsClient.delete(savedQuerySavedObjectType, request.params.id, { + refresh: 'wait_for', + }); - return response.ok({ - body: {}, - }); - } - ); + return response.ok({ + body: {}, + }); + } + ); }; diff --git a/x-pack/plugins/osquery/server/routes/saved_query/find_saved_query_route.ts b/x-pack/plugins/osquery/server/routes/saved_query/find_saved_query_route.ts index 39fd95fb23478..ad4b151554e4d 100644 --- a/x-pack/plugins/osquery/server/routes/saved_query/find_saved_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/saved_query/find_saved_query_route.ts @@ -9,6 +9,7 @@ import { schema } from '@kbn/config-schema'; import type { IRouter } from '@kbn/core/server'; import { omit } from 'lodash'; +import { API_VERSIONS } from '../../../common/constants'; import type { SavedQueryResponse } from './types'; import type { SavedQuerySavedObject } from '../../common/types'; import type { OsqueryAppContext } from '../../lib/osquery_app_context_services'; @@ -18,96 +19,105 @@ import { convertECSMappingToObject } from '../utils'; import { getInstalledSavedQueriesMap } from './utils'; export const findSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { - router.get( - { + router.versioned + .get({ + access: 'public', path: '/api/osquery/saved_queries', - validate: { - query: schema.object({ - page: schema.number({ defaultValue: 1 }), - pageSize: schema.maybe(schema.number()), - sort: schema.string({ defaultValue: 'id' }), - sortOrder: schema.oneOf([schema.literal('asc'), schema.literal('desc')], { - defaultValue: 'desc', - }), - }), - }, options: { tags: [`access:${PLUGIN_ID}-readSavedQueries`] }, - }, - async (context, request, response) => { - const coreContext = await context.core; - const savedObjectsClient = coreContext.savedObjects.client; + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: { + query: schema.object({ + page: schema.number({ defaultValue: 1 }), + pageSize: schema.maybe(schema.number()), + sort: schema.string({ defaultValue: 'id' }), + sortOrder: schema.oneOf([schema.literal('asc'), schema.literal('desc')], { + defaultValue: 'desc', + }), + }), + }, + }, + }, + async (context, request, response) => { + const coreContext = await context.core; + const savedObjectsClient = coreContext.savedObjects.client; - try { - const savedQueries = await savedObjectsClient.find({ - type: savedQuerySavedObjectType, - page: request.query.page, - perPage: request.query.pageSize, - sortField: request.query.sort, - sortOrder: request.query.sortOrder, - }); + try { + const savedQueries = await savedObjectsClient.find({ + type: savedQuerySavedObjectType, + page: request.query.page, + perPage: request.query.pageSize, + sortField: request.query.sort, + sortOrder: request.query.sortOrder, + }); - const prebuiltSavedQueriesMap = await getInstalledSavedQueriesMap( - osqueryContext.service.getPackageService()?.asInternalUser - ); - const savedObjects: SavedQueryResponse[] = savedQueries.saved_objects.map((savedObject) => { - // eslint-disable-next-line @typescript-eslint/naming-convention - const ecs_mapping = savedObject.attributes.ecs_mapping; + const prebuiltSavedQueriesMap = await getInstalledSavedQueriesMap( + osqueryContext.service.getPackageService()?.asInternalUser + ); + const savedObjects: SavedQueryResponse[] = savedQueries.saved_objects.map( + (savedObject) => { + // eslint-disable-next-line @typescript-eslint/naming-convention + const ecs_mapping = savedObject.attributes.ecs_mapping; - savedObject.attributes.prebuilt = !!prebuiltSavedQueriesMap[savedObject.id]; + savedObject.attributes.prebuilt = !!prebuiltSavedQueriesMap[savedObject.id]; - if (ecs_mapping) { - // @ts-expect-error update types - savedObject.attributes.ecs_mapping = convertECSMappingToObject(ecs_mapping); - } + if (ecs_mapping) { + // @ts-expect-error update types + savedObject.attributes.ecs_mapping = convertECSMappingToObject(ecs_mapping); + } - const { - created_at: createdAt, - created_by: createdBy, - description, - id, - interval, - platform, - query, - removed, - snapshot, - version, - ecs_mapping: ecsMapping, - updated_at: updatedAt, - updated_by: updatedBy, - prebuilt, - } = savedObject.attributes; + const { + created_at: createdAt, + created_by: createdBy, + description, + id, + interval, + platform, + query, + removed, + snapshot, + version, + ecs_mapping: ecsMapping, + updated_at: updatedAt, + updated_by: updatedBy, + prebuilt, + } = savedObject.attributes; - return { - created_at: createdAt, - created_by: createdBy, - description, - id, - removed, - snapshot, - version, - ecs_mapping: ecsMapping, - interval, - platform, - query, - updated_at: updatedAt, - updated_by: updatedBy, - prebuilt, - saved_object_id: savedObject.id, - }; - }); + return { + created_at: createdAt, + created_by: createdBy, + description, + id, + removed, + snapshot, + version, + ecs_mapping: ecsMapping, + interval, + platform, + query, + updated_at: updatedAt, + updated_by: updatedBy, + prebuilt, + saved_object_id: savedObject.id, + }; + } + ); - return response.ok({ - body: { - ...omit(savedQueries, 'saved_objects'), - data: savedObjects, - }, - }); - } catch (e) { - return response.customError({ - statusCode: e.statusCode || e.output?.statusCode || 500, - body: e, - }); + return response.ok({ + body: { + ...omit(savedQueries, 'saved_objects'), + data: savedObjects, + }, + }); + } catch (e) { + return response.customError({ + statusCode: e.statusCode || e.output?.statusCode || 500, + body: e, + }); + } } - } - ); + ); }; diff --git a/x-pack/plugins/osquery/server/routes/saved_query/read_saved_query_route.ts b/x-pack/plugins/osquery/server/routes/saved_query/read_saved_query_route.ts index ecaec2f2810a2..81d14e160a1a8 100644 --- a/x-pack/plugins/osquery/server/routes/saved_query/read_saved_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/saved_query/read_saved_query_route.ts @@ -7,6 +7,7 @@ import { schema } from '@kbn/config-schema'; import type { IRouter } from '@kbn/core/server'; +import { API_VERSIONS } from '../../../common/constants'; import type { SavedQueryResponse } from './types'; import type { SavedQuerySavedObject } from '../../common/types'; import { isSavedQueryPrebuilt } from './utils'; @@ -16,77 +17,84 @@ import { savedQuerySavedObjectType } from '../../../common/types'; import { convertECSMappingToObject } from '../utils'; export const readSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { - router.get( - { + router.versioned + .get({ + access: 'public', path: '/api/osquery/saved_queries/{id}', - validate: { - params: schema.object({ - id: schema.string(), - }), - }, options: { tags: [`access:${PLUGIN_ID}-readSavedQueries`] }, - }, - async (context, request, response) => { - const coreContext = await context.core; - const savedObjectsClient = coreContext.savedObjects.client; - - const savedQuery = await savedObjectsClient.get( - savedQuerySavedObjectType, - request.params.id - ); + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: { + params: schema.object({ + id: schema.string(), + }), + }, + }, + }, + async (context, request, response) => { + const coreContext = await context.core; + const savedObjectsClient = coreContext.savedObjects.client; - if (savedQuery.attributes.ecs_mapping) { - // @ts-expect-error update types - savedQuery.attributes.ecs_mapping = convertECSMappingToObject( - savedQuery.attributes.ecs_mapping + const savedQuery = await savedObjectsClient.get( + savedQuerySavedObjectType, + request.params.id ); - } - savedQuery.attributes.prebuilt = await isSavedQueryPrebuilt( - osqueryContext.service.getPackageService()?.asInternalUser, - savedQuery.id - ); + if (savedQuery.attributes.ecs_mapping) { + // @ts-expect-error update types + savedQuery.attributes.ecs_mapping = convertECSMappingToObject( + savedQuery.attributes.ecs_mapping + ); + } - const { - created_at: createdAt, - created_by: createdBy, - description, - id, - interval, - platform, - query, - removed, - snapshot, - version, - ecs_mapping: ecsMapping, - updated_at: updatedAt, - updated_by: updatedBy, - prebuilt, - } = savedQuery.attributes; + savedQuery.attributes.prebuilt = await isSavedQueryPrebuilt( + osqueryContext.service.getPackageService()?.asInternalUser, + savedQuery.id + ); - const data: SavedQueryResponse = { - created_at: createdAt, - created_by: createdBy, - description, - id, - removed, - snapshot, - version, - ecs_mapping: ecsMapping, - interval, - platform, - query, - updated_at: updatedAt, - updated_by: updatedBy, - prebuilt, - saved_object_id: savedQuery.id, - }; + const { + created_at: createdAt, + created_by: createdBy, + description, + id, + interval, + platform, + query, + removed, + snapshot, + version, + ecs_mapping: ecsMapping, + updated_at: updatedAt, + updated_by: updatedBy, + prebuilt, + } = savedQuery.attributes; - return response.ok({ - body: { - data, - }, - }); - } - ); + const data: SavedQueryResponse = { + created_at: createdAt, + created_by: createdBy, + description, + id, + removed, + snapshot, + version, + ecs_mapping: ecsMapping, + interval, + platform, + query, + updated_at: updatedAt, + updated_by: updatedBy, + prebuilt, + saved_object_id: savedQuery.id, + }; + + return response.ok({ + body: { + data, + }, + }); + } + ); }; diff --git a/x-pack/plugins/osquery/server/routes/saved_query/update_saved_query_route.ts b/x-pack/plugins/osquery/server/routes/saved_query/update_saved_query_route.ts index 4cfe3e74295df..758016830b892 100644 --- a/x-pack/plugins/osquery/server/routes/saved_query/update_saved_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/saved_query/update_saved_query_route.ts @@ -9,6 +9,7 @@ import { filter, some } from 'lodash'; import { schema } from '@kbn/config-schema'; import type { IRouter } from '@kbn/core/server'; +import { API_VERSIONS } from '../../../common/constants'; import { isSavedQueryPrebuilt } from './utils'; import { PLUGIN_ID } from '../../../common'; import { savedQuerySavedObjectType } from '../../../common/types'; @@ -17,134 +18,144 @@ import { convertECSMappingToArray, convertECSMappingToObject } from '../utils'; import type { UpdateSavedQueryResponse } from './types'; export const updateSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { - router.put( - { + router.versioned + .put({ + access: 'public', path: '/api/osquery/saved_queries/{id}', - validate: { - params: schema.object({ - id: schema.string(), - }), - body: schema.object( - { - id: schema.string(), - query: schema.string(), - description: schema.maybe(schema.string()), - interval: schema.maybe(schema.number()), - snapshot: schema.maybe(schema.boolean()), - removed: schema.maybe(schema.boolean()), - platform: schema.maybe(schema.string()), - version: schema.maybe(schema.string()), - ecs_mapping: schema.maybe( - schema.recordOf( - schema.string(), - schema.object({ - field: schema.maybe(schema.string()), - value: schema.maybe( - schema.oneOf([schema.string(), schema.arrayOf(schema.string())]) - ), - }) - ) + options: { tags: [`access:${PLUGIN_ID}-writeSavedQueries`] }, + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: { + params: schema.object({ + id: schema.string(), + }), + body: schema.object( + { + id: schema.string(), + query: schema.string(), + description: schema.maybe(schema.string()), + interval: schema.maybe(schema.number()), + snapshot: schema.maybe(schema.boolean()), + removed: schema.maybe(schema.boolean()), + platform: schema.maybe(schema.string()), + version: schema.maybe(schema.string()), + ecs_mapping: schema.maybe( + schema.recordOf( + schema.string(), + schema.object({ + field: schema.maybe(schema.string()), + value: schema.maybe( + schema.oneOf([schema.string(), schema.arrayOf(schema.string())]) + ), + }) + ) + ), + }, + { unknowns: 'allow' } ), }, - { unknowns: 'allow' } - ), + }, }, - options: { tags: [`access:${PLUGIN_ID}-writeSavedQueries`] }, - }, - async (context, request, response) => { - const coreContext = await context.core; - const savedObjectsClient = coreContext.savedObjects.client; - const currentUser = await osqueryContext.security.authc.getCurrentUser(request)?.username; - - const { - id, - description, - platform, - query, - version, - interval, - snapshot, - removed, - // eslint-disable-next-line @typescript-eslint/naming-convention - ecs_mapping, - } = request.body; - - const isPrebuilt = await isSavedQueryPrebuilt( - osqueryContext.service.getPackageService()?.asInternalUser, - request.params.id - ); - - if (isPrebuilt) { - return response.conflict({ body: `Elastic prebuilt Saved query cannot be updated.` }); - } - - const conflictingEntries = await savedObjectsClient.find<{ id: string }>({ - type: savedQuerySavedObjectType, - filter: `${savedQuerySavedObjectType}.attributes.id: "${id}"`, - }); - - if ( - some( - filter(conflictingEntries.saved_objects, (soObject) => soObject.id !== request.params.id), - ['attributes.id', id] - ) - ) { - return response.conflict({ body: `Saved query with id "${id}" already exists.` }); - } + async (context, request, response) => { + const coreContext = await context.core; + const savedObjectsClient = coreContext.savedObjects.client; + const currentUser = await osqueryContext.security.authc.getCurrentUser(request)?.username; - const updatedSavedQuerySO = await savedObjectsClient.update( - savedQuerySavedObjectType, - request.params.id, - { + const { id, - description: description || '', + description, platform, query, version, interval, snapshot, removed, - ecs_mapping: convertECSMappingToArray(ecs_mapping), - updated_by: currentUser, - updated_at: new Date().toISOString(), - }, - { - refresh: 'wait_for', + // eslint-disable-next-line @typescript-eslint/naming-convention + ecs_mapping, + } = request.body; + + const isPrebuilt = await isSavedQueryPrebuilt( + osqueryContext.service.getPackageService()?.asInternalUser, + request.params.id + ); + + if (isPrebuilt) { + return response.conflict({ body: `Elastic prebuilt Saved query cannot be updated.` }); } - ); - if (ecs_mapping || updatedSavedQuerySO.attributes.ecs_mapping) { - // @ts-expect-error update types - updatedSavedQuerySO.attributes.ecs_mapping = - ecs_mapping || - (updatedSavedQuerySO.attributes.ecs_mapping && - // @ts-expect-error update types - convertECSMappingToObject(updatedSavedQuerySO.attributes.ecs_mapping)) || - {}; - } + const conflictingEntries = await savedObjectsClient.find<{ id: string }>({ + type: savedQuerySavedObjectType, + filter: `${savedQuerySavedObjectType}.attributes.id: "${id}"`, + }); - const { attributes } = updatedSavedQuerySO; + if ( + some( + filter( + conflictingEntries.saved_objects, + (soObject) => soObject.id !== request.params.id + ), + ['attributes.id', id] + ) + ) { + return response.conflict({ body: `Saved query with id "${id}" already exists.` }); + } - const data: Partial = { - description: attributes.description, - id: attributes.id, - removed: attributes.removed, - snapshot: attributes.snapshot, - version: attributes.version, - ecs_mapping: attributes.ecs_mapping, - interval: attributes.interval, - platform: attributes.platform, - query: attributes.query, - updated_at: attributes.updated_at, - updated_by: attributes.updated_by, - saved_object_id: updatedSavedQuerySO.id, - }; + const updatedSavedQuerySO = await savedObjectsClient.update( + savedQuerySavedObjectType, + request.params.id, + { + id, + description: description || '', + platform, + query, + version, + interval, + snapshot, + removed, + ecs_mapping: convertECSMappingToArray(ecs_mapping), + updated_by: currentUser, + updated_at: new Date().toISOString(), + }, + { + refresh: 'wait_for', + } + ); - return response.ok({ - body: { - data, - }, - }); - } - ); + if (ecs_mapping || updatedSavedQuerySO.attributes.ecs_mapping) { + // @ts-expect-error update types + updatedSavedQuerySO.attributes.ecs_mapping = + ecs_mapping || + (updatedSavedQuerySO.attributes.ecs_mapping && + // @ts-expect-error update types + convertECSMappingToObject(updatedSavedQuerySO.attributes.ecs_mapping)) || + {}; + } + + const { attributes } = updatedSavedQuerySO; + + const data: Partial = { + description: attributes.description, + id: attributes.id, + removed: attributes.removed, + snapshot: attributes.snapshot, + version: attributes.version, + ecs_mapping: attributes.ecs_mapping, + interval: attributes.interval, + platform: attributes.platform, + query: attributes.query, + updated_at: attributes.updated_at, + updated_by: attributes.updated_by, + saved_object_id: updatedSavedQuerySO.id, + }; + + return response.ok({ + body: { + data, + }, + }); + } + ); }; diff --git a/x-pack/plugins/osquery/server/routes/status/create_status_route.ts b/x-pack/plugins/osquery/server/routes/status/create_status_route.ts index 785b9540e173f..2bab8d82bbab8 100644 --- a/x-pack/plugins/osquery/server/routes/status/create_status_route.ts +++ b/x-pack/plugins/osquery/server/routes/status/create_status_route.ts @@ -15,6 +15,7 @@ import { AGENT_POLICY_SAVED_OBJECT_TYPE, } from '@kbn/fleet-plugin/common'; import type { IRouter } from '@kbn/core/server'; +import { API_VERSIONS } from '../../../common/constants'; import { packSavedObjectType } from '../../../common/types'; import { PLUGIN_ID, OSQUERY_INTEGRATION_NAME } from '../../../common'; import type { OsqueryAppContext } from '../../lib/osquery_app_context_services'; @@ -22,183 +23,188 @@ import { convertPackQueriesToSO } from '../pack/utils'; import { getInternalSavedObjectsClient } from '../utils'; export const createStatusRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { - router.get( - { + router.versioned + .get({ + access: 'internal', path: '/internal/osquery/status', - validate: false, options: { tags: [`access:${PLUGIN_ID}-read`] }, - }, - async (context, request, response) => { - const coreContext = await context.core; - const esClient = coreContext.elasticsearch.client.asInternalUser; - const internalSavedObjectsClient = await getInternalSavedObjectsClient( - osqueryContext.getStartServices - ); - const packageService = osqueryContext.service.getPackageService()?.asInternalUser; - const packagePolicyService = osqueryContext.service.getPackagePolicyService(); - const agentPolicyService = osqueryContext.service.getAgentPolicyService(); - - const packageInfo = await packageService?.getInstallation(OSQUERY_INTEGRATION_NAME); - - if (packageInfo?.install_version && satisfies(packageInfo?.install_version, '<0.6.0')) { - try { - const policyPackages = await packagePolicyService?.list(internalSavedObjectsClient, { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${OSQUERY_INTEGRATION_NAME}`, - perPage: 10000, - page: 1, - }); - - const migrationObject = reduce( - policyPackages?.items, - (acc, policy) => { - if (acc.agentPolicyToPackage[policy.policy_id]) { - acc.packagePoliciesToDelete.push(policy.id); - } else { - acc.agentPolicyToPackage[policy.policy_id] = policy.id; - } - - const packagePolicyName = policy.name; - const currentOsqueryManagerNamePacksCount = filter( - Object.keys(acc.packs), - (packName) => packName.startsWith(OSQUERY_INTEGRATION_NAME) - ).length; - - const packName = packagePolicyName.startsWith(OSQUERY_INTEGRATION_NAME) - ? `osquery_manager-1_${currentOsqueryManagerNamePacksCount + 1}` - : packagePolicyName; - - if (has(policy, 'inputs[0].streams[0]')) { - if (!acc.packs[packName]) { - acc.packs[packName] = { - policy_ids: [policy.policy_id], - enabled: !packName.startsWith(OSQUERY_INTEGRATION_NAME), - name: packName, - description: policy.description, - queries: reduce>( - policy.inputs[0].streams, - (queries, stream) => { - if (stream.compiled_stream?.id) { - const { id: queryId, ...query } = stream.compiled_stream; - queries[queryId] = query; - } - - return queries; - }, - {} - ), - }; + }) + .addVersion( + { + version: API_VERSIONS.internal.v1, + validate: false, + }, + async (context, request, response) => { + const coreContext = await context.core; + const esClient = coreContext.elasticsearch.client.asInternalUser; + const internalSavedObjectsClient = await getInternalSavedObjectsClient( + osqueryContext.getStartServices + ); + const packageService = osqueryContext.service.getPackageService()?.asInternalUser; + const packagePolicyService = osqueryContext.service.getPackagePolicyService(); + const agentPolicyService = osqueryContext.service.getAgentPolicyService(); + + const packageInfo = await packageService?.getInstallation(OSQUERY_INTEGRATION_NAME); + + if (packageInfo?.install_version && satisfies(packageInfo?.install_version, '<0.6.0')) { + try { + const policyPackages = await packagePolicyService?.list(internalSavedObjectsClient, { + kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${OSQUERY_INTEGRATION_NAME}`, + perPage: 10000, + page: 1, + }); + + const migrationObject = reduce( + policyPackages?.items, + (acc, policy) => { + if (acc.agentPolicyToPackage[policy.policy_id]) { + acc.packagePoliciesToDelete.push(policy.id); } else { - acc.packs[packName].policy_ids.push(policy.policy_id); + acc.agentPolicyToPackage[policy.policy_id] = policy.id; } - } - return acc; - }, - { - packs: {} as Record< - string, - { - policy_ids: string[]; - enabled: boolean; - name: string; - description?: string; - queries: Record; + const packagePolicyName = policy.name; + const currentOsqueryManagerNamePacksCount = filter( + Object.keys(acc.packs), + (packName) => packName.startsWith(OSQUERY_INTEGRATION_NAME) + ).length; + + const packName = packagePolicyName.startsWith(OSQUERY_INTEGRATION_NAME) + ? `osquery_manager-1_${currentOsqueryManagerNamePacksCount + 1}` + : packagePolicyName; + + if (has(policy, 'inputs[0].streams[0]')) { + if (!acc.packs[packName]) { + acc.packs[packName] = { + policy_ids: [policy.policy_id], + enabled: !packName.startsWith(OSQUERY_INTEGRATION_NAME), + name: packName, + description: policy.description, + queries: reduce>( + policy.inputs[0].streams, + (queries, stream) => { + if (stream.compiled_stream?.id) { + const { id: queryId, ...query } = stream.compiled_stream; + queries[queryId] = query; + } + + return queries; + }, + {} + ), + }; + } else { + acc.packs[packName].policy_ids.push(policy.policy_id); + } } - >, - agentPolicyToPackage: {} as Record, - packagePoliciesToDelete: [] as string[], - } - ); - - await packageService?.ensureInstalledPackage({ - pkgName: OSQUERY_INTEGRATION_NAME, - }); - - const agentPolicyIds = uniq(map(policyPackages?.items, 'policy_id')); - const agentPolicies = mapKeys( - await agentPolicyService?.getByIds(internalSavedObjectsClient, agentPolicyIds), - 'id' - ); - - await Promise.all( - map(migrationObject.packs, async (packObject) => { - await internalSavedObjectsClient.create( - packSavedObjectType, - { - name: packObject.name, - description: packObject.description, - queries: convertPackQueriesToSO(packObject.queries), - enabled: packObject.enabled, - created_at: new Date().toISOString(), - created_by: 'system', - updated_at: new Date().toISOString(), - updated_by: 'system', - }, - { - references: packObject.policy_ids.map((policyId: string) => ({ - id: policyId, - name: agentPolicies[policyId].name, - type: AGENT_POLICY_SAVED_OBJECT_TYPE, - })), - refresh: 'wait_for', - } - ); - }) - ); - - // delete unnecessary package policies - await packagePolicyService?.delete( - internalSavedObjectsClient, - esClient, - migrationObject.packagePoliciesToDelete - ); - - // updatePackagePolicies - await Promise.all( - map(migrationObject.agentPolicyToPackage, async (value, key) => { - const agentPacks = filter(migrationObject.packs, (pack) => - pack.policy_ids.includes(key) - ); - await packagePolicyService?.upgrade(internalSavedObjectsClient, esClient, [value]); - const packagePolicy = await packagePolicyService?.get( - internalSavedObjectsClient, - value - ); - - if (packagePolicy) { - return packagePolicyService?.update( + + return acc; + }, + { + packs: {} as Record< + string, + { + policy_ids: string[]; + enabled: boolean; + name: string; + description?: string; + queries: Record; + } + >, + agentPolicyToPackage: {} as Record, + packagePoliciesToDelete: [] as string[], + } + ); + + await packageService?.ensureInstalledPackage({ + pkgName: OSQUERY_INTEGRATION_NAME, + }); + + const agentPolicyIds = uniq(map(policyPackages?.items, 'policy_id')); + const agentPolicies = mapKeys( + await agentPolicyService?.getByIds(internalSavedObjectsClient, agentPolicyIds), + 'id' + ); + + await Promise.all( + map(migrationObject.packs, async (packObject) => { + await internalSavedObjectsClient.create( + packSavedObjectType, + { + name: packObject.name, + description: packObject.description, + queries: convertPackQueriesToSO(packObject.queries), + enabled: packObject.enabled, + created_at: new Date().toISOString(), + created_by: 'system', + updated_at: new Date().toISOString(), + updated_by: 'system', + }, + { + references: packObject.policy_ids.map((policyId: string) => ({ + id: policyId, + name: agentPolicies[policyId].name, + type: AGENT_POLICY_SAVED_OBJECT_TYPE, + })), + refresh: 'wait_for', + } + ); + }) + ); + + // delete unnecessary package policies + await packagePolicyService?.delete( + internalSavedObjectsClient, + esClient, + migrationObject.packagePoliciesToDelete + ); + + // updatePackagePolicies + await Promise.all( + map(migrationObject.agentPolicyToPackage, async (value, key) => { + const agentPacks = filter(migrationObject.packs, (pack) => + pack.policy_ids.includes(key) + ); + await packagePolicyService?.upgrade(internalSavedObjectsClient, esClient, [value]); + const packagePolicy = await packagePolicyService?.get( internalSavedObjectsClient, - esClient, - packagePolicy.id, - produce(packagePolicy, (draft) => { - unset(draft, 'id'); - - set(draft, 'name', 'osquery_manager-1'); - - set(draft, 'inputs[0]', { - enabled: true, - policy_template: OSQUERY_INTEGRATION_NAME, - streams: [], - type: 'osquery', - }); - - each(agentPacks, (agentPack) => { - set(draft, `inputs[0].config.osquery.value.packs.${agentPack.name}`, { - queries: agentPack.queries, + value + ); + + if (packagePolicy) { + return packagePolicyService?.update( + internalSavedObjectsClient, + esClient, + packagePolicy.id, + produce(packagePolicy, (draft) => { + unset(draft, 'id'); + + set(draft, 'name', 'osquery_manager-1'); + + set(draft, 'inputs[0]', { + enabled: true, + policy_template: OSQUERY_INTEGRATION_NAME, + streams: [], + type: 'osquery', }); - }); - return draft; - }) - ); - } - }) - ); - // eslint-disable-next-line no-empty - } catch (e) {} - } + each(agentPacks, (agentPack) => { + set(draft, `inputs[0].config.osquery.value.packs.${agentPack.name}`, { + queries: agentPack.queries, + }); + }); + + return draft; + }) + ); + } + }) + ); + // eslint-disable-next-line no-empty + } catch (e) {} + } - return response.ok({ body: packageInfo }); - } - ); + return response.ok({ body: packageInfo }); + } + ); }; diff --git a/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_metadata_generator.ts b/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_metadata_generator.ts index 7d1b6f9086bcd..feb53ffd042b7 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_metadata_generator.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_generators/endpoint_metadata_generator.ts @@ -215,6 +215,7 @@ export class EndpointMetadataGenerator extends BaseDataGenerator { }, }, }, + last_checkin: new Date().toISOString(), }; return merge(hostInfo, overrides); } diff --git a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_hosts.ts b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_hosts.ts index d778e1cde027f..eed88ea4d44d2 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_hosts.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_hosts.ts @@ -211,7 +211,7 @@ export async function indexEndpointHostDocs({ await client .index({ index: metadataIndex, - body: hostMetadata, + document: hostMetadata, op_type: 'create', refresh: 'wait_for', }) @@ -225,7 +225,7 @@ export async function indexEndpointHostDocs({ await client .index({ index: policyResponseIndex, - body: hostPolicyResponse, + document: hostPolicyResponse, op_type: 'create', refresh: 'wait_for', }) @@ -281,11 +281,9 @@ export const deleteIndexedEndpointHosts = async ( }; if (indexedData.hosts.length) { - const body = { - query: { - bool: { - filter: [{ terms: { 'agent.id': indexedData.hosts.map((host) => host.agent.id) } }], - }, + const query = { + bool: { + filter: [{ terms: { 'agent.id': indexedData.hosts.map((host) => host.agent.id) } }], }, }; @@ -293,7 +291,7 @@ export const deleteIndexedEndpointHosts = async ( .deleteByQuery({ index: indexedData.metadataIndex, wait_for_completion: true, - body, + query, }) .catch(wrapErrorAndRejectPromise); @@ -302,7 +300,7 @@ export const deleteIndexedEndpointHosts = async ( .deleteByQuery({ index: metadataCurrentIndexPattern, wait_for_completion: true, - body, + query, }) .catch(wrapErrorAndRejectPromise); } @@ -312,19 +310,17 @@ export const deleteIndexedEndpointHosts = async ( .deleteByQuery({ index: indexedData.policyResponseIndex, wait_for_completion: true, - body: { - query: { - bool: { - filter: [ - { - terms: { - 'agent.id': indexedData.policyResponses.map( - (policyResponse) => policyResponse.agent.id - ), - }, + query: { + bool: { + filter: [ + { + terms: { + 'agent.id': indexedData.policyResponses.map( + (policyResponse) => policyResponse.agent.id + ), }, - ], - }, + }, + ], }, }, }) diff --git a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts index fe1b22644d8e1..1d7a69186421f 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts @@ -466,6 +466,7 @@ export interface FileUploadMetadata { transithash: { sha256: string; }; + '@timestamp': string; } export type UploadedFileInfo = Pick< diff --git a/x-pack/plugins/security_solution/common/endpoint/types/index.ts b/x-pack/plugins/security_solution/common/endpoint/types/index.ts index aa1881195a0b6..3e48770cd5b39 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/index.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/index.ts @@ -474,7 +474,7 @@ export type PolicyInfo = Immutable<{ }>; // Host Information as returned by the Host Details API. -// NOTE: `HostInfo` type is the original and defined as Immutable. +// NOTE:The `HostInfo` type is the original and defined as Immutable. export interface HostInfoInterface { metadata: HostMetadataInterface; host_status: HostStatus; @@ -485,7 +485,7 @@ export interface HostInfoInterface { */ configured: PolicyInfo; /** - * Last reported running in agent (may lag behind configured) + * Last reported running in agent (might lag behind configured) */ applied: PolicyInfo; }; @@ -494,14 +494,22 @@ export interface HostInfoInterface { */ endpoint: PolicyInfo; }; + /** + * The time when the Elastic Agent associated with this Endpoint host checked in with fleet + * Conceptually the value is the same as Agent['last_checkin'] if present, but we fall back to + * UnitedAgentMetadataPersistedData['united']['endpoint']['metadata']['@timestamp'] + * if `Agent.last_checkin` value is `undefined` + */ + last_checkin: string; } export type HostInfo = Immutable; // Host metadata document streamed up to ES by the Endpoint running on host machines. -// NOTE: `HostMetadata` type is the original and defined as Immutable. If needing to +// NOTE: The `HostMetadata` type is the original and defined as Immutable. If you need to // work with metadata that is not mutable, use `HostMetadataInterface` export type HostMetadata = Immutable; + export interface HostMetadataInterface { '@timestamp': number; event: { diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.test.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.test.ts index 3eeb09d1b8188..dade311fa6b49 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.test.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.test.ts @@ -13,6 +13,7 @@ import type { CellActionExecutionContext } from '@kbn/cell-actions'; import { GEO_FIELD_TYPE } from '../../../timelines/components/timeline/body/renderers/constants'; import { createStartServicesMock } from '../../../common/lib/kibana/kibana_react.mock'; import { set } from 'lodash/fp'; +import { KBN_FIELD_TYPES } from '@kbn/field-types'; const services = createStartServicesMock(); const mockWarningToast = services.notifications.toasts.addWarning; @@ -27,7 +28,7 @@ const value = 'the-value'; const context = { data: [ { - field: { name: 'user.name', type: 'text' }, + field: { name: 'user.name', type: 'string' }, value, }, ], @@ -75,6 +76,7 @@ describe('createAddToTimelineCellAction', () => { it('should return true if everything is okay', async () => { expect(await addToTimelineAction.isCompatible(context)).toEqual(true); }); + it('should return false if field not allowed', async () => { expect( await addToTimelineAction.isCompatible({ @@ -88,6 +90,20 @@ describe('createAddToTimelineCellAction', () => { }) ).toEqual(false); }); + + it('should return false if Kbn type is unsupported', async () => { + expect( + await addToTimelineAction.isCompatible({ + ...context, + data: [ + { + ...context.data[0], + field: { ...context.data[0].field, type: KBN_FIELD_TYPES.DATE_RANGE }, + }, + ], + }) + ).toEqual(false); + }); }); describe('execute', () => { @@ -190,6 +206,20 @@ describe('createAddToTimelineCellAction', () => { expect(mockWarningToast).toHaveBeenCalled(); }); + it('should show warning if value type is unsupported', async () => { + await addToTimelineAction.execute({ + ...context, + data: [ + { + ...context.data[0], + value: {}, + }, + ], + }); + expect(mockDispatch).not.toHaveBeenCalled(); + expect(mockWarningToast).toHaveBeenCalled(); + }); + describe('should execute correctly when negateFilters is provided', () => { it('should not exclude if negateFilters is false', async () => { await addToTimelineAction.execute({ diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.ts index 0f5dd25ed6b64..9ce248701a75d 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.ts @@ -7,6 +7,14 @@ import { createCellActionFactory } from '@kbn/cell-actions'; import type { CellActionTemplate } from '@kbn/cell-actions'; +import { + isTypeSupportedByDefaultActions, + isValueSupportedByDefaultActions, + filterOutNullableValues, + valueToArray, +} from '@kbn/cell-actions/src/actions/utils'; +import { ACTION_INCOMPATIBLE_VALUE_WARNING } from '@kbn/cell-actions/src/actions/translations'; +import type { KBN_FIELD_TYPES } from '@kbn/field-types'; import { addProvider } from '../../../timelines/store/timeline/actions'; import { TimelineId } from '../../../../common/types'; import type { SecurityAppStore } from '../../../common/store'; @@ -44,15 +52,24 @@ export const createAddToTimelineCellActionFactory = createCellActionFactory( return ( data.length === 1 && // TODO Add support for multiple values fieldHasCellActions(field.name) && - isValidDataProviderField(field.name, field.type) + isValidDataProviderField(field.name, field.type) && + isTypeSupportedByDefaultActions(field.type as KBN_FIELD_TYPES) ); }, + execute: async ({ data, metadata }) => { const { name, type } = data[0]?.field; - const value = data[0]?.value; + const rawValue = data[0]?.value; + const value = filterOutNullableValues(valueToArray(rawValue)); + + if (!isValueSupportedByDefaultActions(value)) { + notificationsService.toasts.addWarning({ + title: ACTION_INCOMPATIBLE_VALUE_WARNING, + }); + return; + } - const values = Array.isArray(value) ? value : [value]; - const [firstValue, ...andValues] = values; + const [firstValue, ...andValues] = value; const [dataProvider] = createDataProviders({ contextId: TimelineId.active, @@ -81,10 +98,7 @@ export const createAddToTimelineCellActionFactory = createCellActionFactory( if (dataProvider) { store.dispatch(addProvider({ id: TimelineId.active, providers: [dataProvider] })); - let messageValue = ''; - if (value != null) { - messageValue = Array.isArray(value) ? value.join(', ') : value.toString(); - } + const messageValue = value.join(', '); notificationsService.toasts.addSuccess({ title: ADD_TO_TIMELINE_SUCCESS_TITLE(messageValue), }); diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/investigate_in_new_timeline.test.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/investigate_in_new_timeline.test.ts index 97edad91e5a01..d8c83e6a336d9 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/investigate_in_new_timeline.test.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/investigate_in_new_timeline.test.ts @@ -13,6 +13,7 @@ import type { CellActionExecutionContext } from '@kbn/cell-actions'; import { GEO_FIELD_TYPE } from '../../../timelines/components/timeline/body/renderers/constants'; import { createStartServicesMock } from '../../../common/lib/kibana/kibana_react.mock'; import { timelineActions } from '../../../timelines/store/timeline'; +import { KBN_FIELD_TYPES } from '@kbn/field-types'; const services = createStartServicesMock(); const mockWarningToast = services.notifications.toasts.addWarning; @@ -27,7 +28,7 @@ const value = 'the-value'; const context = { data: [ { - field: { name: 'user.name', type: 'text' }, + field: { name: 'user.name', type: 'string' }, value, }, ], @@ -78,6 +79,7 @@ describe('createAddToNewTimelineCellAction', () => { it('should return true if everything is okay', async () => { expect(await addToTimelineAction.isCompatible(context)).toEqual(true); }); + it('should return false if field not allowed', async () => { expect( await addToTimelineAction.isCompatible({ @@ -91,6 +93,20 @@ describe('createAddToNewTimelineCellAction', () => { }) ).toEqual(false); }); + + it('should return false if Kbn type is unsupported', async () => { + expect( + await addToTimelineAction.isCompatible({ + ...context, + data: [ + { + ...context.data[0], + field: { ...context.data[0].field, type: KBN_FIELD_TYPES.NESTED }, + }, + ], + }) + ).toEqual(false); + }); }); describe('execute', () => { @@ -114,6 +130,20 @@ describe('createAddToNewTimelineCellAction', () => { expect(mockWarningToast).toHaveBeenCalled(); }); + it('should show warning if value type is unsupported', async () => { + await addToTimelineAction.execute({ + ...context, + data: [ + { + ...context.data[0], + value: [[[]]], + }, + ], + }); + expect(mockDispatch).not.toHaveBeenCalled(); + expect(mockWarningToast).toHaveBeenCalled(); + }); + describe('should execute correctly when negateFilters is provided', () => { it('should not exclude if negateFilters is false', async () => { await addToTimelineAction.execute({ diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/investigate_in_new_timeline.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/investigate_in_new_timeline.ts index b24c0c39ce365..0ba3cf3ffc8a8 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/investigate_in_new_timeline.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/investigate_in_new_timeline.ts @@ -6,6 +6,14 @@ */ import { createCellActionFactory, type CellActionTemplate } from '@kbn/cell-actions'; +import type { KBN_FIELD_TYPES } from '@kbn/field-types'; +import { + isTypeSupportedByDefaultActions, + isValueSupportedByDefaultActions, + valueToArray, + filterOutNullableValues, +} from '@kbn/cell-actions/src/actions/utils'; +import { ACTION_INCOMPATIBLE_VALUE_WARNING } from '@kbn/cell-actions/src/actions/translations'; import { timelineActions } from '../../../timelines/store/timeline'; import { addProvider, showTimeline } from '../../../timelines/store/timeline/actions'; import { TimelineId } from '../../../../common/types'; @@ -44,12 +52,21 @@ export const createInvestigateInNewTimelineCellActionFactory = createCellActionF return ( data.length === 1 && // TODO Add support for multiple values fieldHasCellActions(field.name) && - isValidDataProviderField(field.name, field.type) + isValidDataProviderField(field.name, field.type) && + isTypeSupportedByDefaultActions(field.type as KBN_FIELD_TYPES) ); }, execute: async ({ data, metadata }) => { const field = data[0]?.field; - const value = data[0]?.value; + const rawValue = data[0]?.value; + const value = filterOutNullableValues(valueToArray(rawValue)); + + if (!isValueSupportedByDefaultActions(value)) { + notificationsService.toasts.addWarning({ + title: ACTION_INCOMPATIBLE_VALUE_WARNING, + }); + return; + } const dataProviders = createDataProviders({ diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/data_provider.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/data_provider.ts index 4c79ed3cc56cb..ac50c5de28ef5 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/data_provider.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/data_provider.ts @@ -5,7 +5,6 @@ * 2.0. */ -import type { CellActionFieldValue } from '@kbn/cell-actions/src/types'; import { escapeDataProviderId } from '@kbn/securitysolution-t-grid'; import type { Serializable } from '@kbn/utility-types'; @@ -61,7 +60,7 @@ export interface CreateDataProviderParams { field?: string; fieldFormat?: string; fieldType?: string; - values: CellActionFieldValue; + values: string | string[] | number | number[] | boolean | boolean[]; sourceParamType?: Serializable; negate?: boolean; } @@ -78,7 +77,11 @@ export const createDataProviders = ({ }: CreateDataProviderParams) => { if (field == null) return null; - const arrayValues = Array.isArray(values) ? (values.length > 0 ? values : [null]) : [values]; + const arrayValues: Array = Array.isArray(values) + ? values.length > 0 + ? values + : [null] + : [values]; return arrayValues.reduce((dataProviders, rawValue, index) => { let id: string = ''; diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/discover/add_to_timeline.test.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/discover/add_to_timeline.test.ts index d61a1ab64bdb8..f8c62857e3e24 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/discover/add_to_timeline.test.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/discover/add_to_timeline.test.ts @@ -29,7 +29,7 @@ const store = { const value = 'the-value'; const context = { - data: [{ field: { name: 'user.name', type: 'text' }, value }], + data: [{ field: { name: 'user.name', type: 'string' }, value }], } as CellActionExecutionContext; const defaultDataProvider = { diff --git a/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/cell_action/copy_to_clipboard.test.ts b/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/cell_action/copy_to_clipboard.test.ts index 6df6f6b00b177..ce4655bd6afe2 100644 --- a/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/cell_action/copy_to_clipboard.test.ts +++ b/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/cell_action/copy_to_clipboard.test.ts @@ -19,7 +19,7 @@ describe('createCopyToClipboardCellActionFactory', () => { const copyToClipboardActionFactory = createCopyToClipboardCellActionFactory({ services }); const copyToClipboardAction = copyToClipboardActionFactory({ id: 'testAction' }); const context = { - data: [{ field: { name: 'user.name', type: 'text' }, value: 'the value' }], + data: [{ field: { name: 'user.name', type: 'string' }, value: 'the value' }], } as CellActionExecutionContext; beforeEach(() => { diff --git a/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/cell_action/copy_to_clipboard.ts b/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/cell_action/copy_to_clipboard.ts index 39d39ea8b4910..e4e30a0576cbb 100644 --- a/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/cell_action/copy_to_clipboard.ts +++ b/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/cell_action/copy_to_clipboard.ts @@ -25,10 +25,7 @@ export const createCopyToClipboardCellActionFactory = ({ isCompatible: async ({ data }) => { const field = data[0]?.field; - return ( - data.length === 1 && // TODO Add support for multiple values - fieldHasCellActions(field.name) - ); + return fieldHasCellActions(field.name); }, }); }; diff --git a/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/discover/copy_to_clipboard.test.ts b/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/discover/copy_to_clipboard.test.ts index 23ca470e11d22..0a4032e9b0136 100644 --- a/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/discover/copy_to_clipboard.test.ts +++ b/x-pack/plugins/security_solution/public/actions/copy_to_clipboard/discover/copy_to_clipboard.test.ts @@ -26,7 +26,7 @@ describe('createCopyToClipboardDiscoverCellActionFactory', () => { const context = { data: [ { - field: { name: 'user.name', type: 'text' }, + field: { name: 'user.name', type: 'string' }, value: 'the value', }, ], diff --git a/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_in.test.ts b/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_in.test.ts index 2e2a511933476..01650820294bd 100644 --- a/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_in.test.ts +++ b/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_in.test.ts @@ -18,10 +18,12 @@ import type { SecurityCellActionExecutionContext } from '../../types'; import { createStartServicesMock } from '../../../common/lib/kibana/kibana_react.mock'; import { TableId } from '@kbn/securitysolution-data-table'; import { TimelineId } from '../../../../common/types'; +import { KBN_FIELD_TYPES } from '@kbn/field-types'; const services = createStartServicesMock(); const mockGlobalFilterManager = services.data.query.filterManager; const mockTimelineFilterManager = createFilterManagerMock(); +const mockWarningToast = services.notifications.toasts.addWarning; const mockState = { ...mockGlobalState, @@ -57,7 +59,7 @@ describe('createFilterInCellActionFactory', () => { const context = { data: [ { - field: { name: 'user.name', type: 'text' }, + field: { name: 'user.name', type: 'string' }, value: 'the value', }, ], @@ -75,18 +77,34 @@ describe('createFilterInCellActionFactory', () => { it('should return true if everything is okay', async () => { expect(await filterInAction.isCompatible(context)).toEqual(true); }); + it('should return false if field not allowed', async () => { expect( await filterInAction.isCompatible({ ...context, data: [ { + ...context.data[0], field: { ...context.data[0].field, name: 'signal.reason' }, }, ], }) ).toEqual(false); }); + + it('should return false if Kbn type is unsupported', async () => { + expect( + await filterInAction.isCompatible({ + ...context, + data: [ + { + ...context.data[0], + field: { ...context.data[0].field, type: KBN_FIELD_TYPES.HISTOGRAM }, + }, + ], + }) + ).toEqual(false); + }); }); describe('execute', () => { @@ -101,6 +119,21 @@ describe('createFilterInCellActionFactory', () => { expect(mockGlobalFilterManager.addFilters).toHaveBeenCalled(); expect(mockTimelineFilterManager.addFilters).not.toHaveBeenCalled(); }); + + it('should show warning if value type is unsupported', async () => { + await filterInAction.execute({ + ...dataTableContext, + data: [ + { + ...context.data[0], + value: { test: '123' }, + }, + ], + }); + expect(mockGlobalFilterManager.addFilters).not.toHaveBeenCalled(); + expect(mockTimelineFilterManager.addFilters).not.toHaveBeenCalled(); + expect(mockWarningToast).toHaveBeenCalled(); + }); }); describe('timeline scope execution', () => { diff --git a/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_in.ts b/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_in.ts index 6285d662fff93..905da379d01ff 100644 --- a/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_in.ts +++ b/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_in.ts @@ -6,6 +6,14 @@ */ import { addFilterIn, addFilterOut, createFilterInActionFactory } from '@kbn/cell-actions'; +import { + isTypeSupportedByDefaultActions, + isValueSupportedByDefaultActions, + valueToArray, + filterOutNullableValues, +} from '@kbn/cell-actions/src/actions/utils'; +import type { KBN_FIELD_TYPES } from '@kbn/field-types'; +import { ACTION_INCOMPATIBLE_VALUE_WARNING } from '@kbn/cell-actions/src/actions/translations'; import type { SecurityAppStore } from '../../../common/store'; import { timelineSelectors } from '../../../timelines/store/timeline'; import { fieldHasCellActions } from '../../utils'; @@ -25,7 +33,11 @@ export const createFilterInCellActionFactory = ({ const getTimelineById = timelineSelectors.getTimelineByIdSelector(); const { filterManager } = services.data.query; - const genericFilterInActionFactory = createFilterInActionFactory({ filterManager }); + const { notifications } = services; + const genericFilterInActionFactory = createFilterInActionFactory({ + filterManager, + notifications, + }); return genericFilterInActionFactory.combine({ type: SecurityCellActionType.FILTER, @@ -34,12 +46,21 @@ export const createFilterInCellActionFactory = ({ return ( data.length === 1 && // TODO Add support for multiple values - fieldHasCellActions(field.name) + fieldHasCellActions(field.name) && + isTypeSupportedByDefaultActions(field.type as KBN_FIELD_TYPES) ); }, execute: async ({ data, metadata }) => { const field = data[0]?.field; - const value = data[0]?.value; + const rawValue = data[0]?.value; + const value = filterOutNullableValues(valueToArray(rawValue)); + + if (!isValueSupportedByDefaultActions(value)) { + notifications.toasts.addWarning({ + title: ACTION_INCOMPATIBLE_VALUE_WARNING, + }); + return; + } if (!field) return; diff --git a/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_out.test.ts b/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_out.test.ts index b4c855c3a4509..088cc944cb1c7 100644 --- a/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_out.test.ts +++ b/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_out.test.ts @@ -18,10 +18,12 @@ import type { SecurityCellActionExecutionContext } from '../../types'; import { createStartServicesMock } from '../../../common/lib/kibana/kibana_react.mock'; import { TimelineId } from '../../../../common/types'; import { TableId } from '@kbn/securitysolution-data-table'; +import { KBN_FIELD_TYPES } from '@kbn/field-types'; const services = createStartServicesMock(); const mockGlobalFilterManager = services.data.query.filterManager; const mockTimelineFilterManager = createFilterManagerMock(); +const mockWarningToast = services.notifications.toasts.addWarning; const mockState = { ...mockGlobalState, @@ -51,7 +53,7 @@ describe('createFilterOutCellActionFactory', () => { const context = { data: [ { - field: { name: 'user.name', type: 'text' }, + field: { name: 'user.name', type: 'string' }, value: 'the value', }, ], @@ -69,6 +71,7 @@ describe('createFilterOutCellActionFactory', () => { it('should return true if everything is okay', async () => { expect(await filterOutAction.isCompatible(context)).toEqual(true); }); + it('should return false if field not allowed', async () => { expect( await filterOutAction.isCompatible({ @@ -81,6 +84,20 @@ describe('createFilterOutCellActionFactory', () => { }) ).toEqual(false); }); + + it('should return false if Kbn type is unsupported', async () => { + expect( + await filterOutAction.isCompatible({ + ...context, + data: [ + { + ...context.data[0], + field: { ...context.data[0].field, type: KBN_FIELD_TYPES.OBJECT }, + }, + ], + }) + ).toEqual(false); + }); }); describe('execute', () => { @@ -95,6 +112,21 @@ describe('createFilterOutCellActionFactory', () => { expect(mockGlobalFilterManager.addFilters).toHaveBeenCalled(); expect(mockTimelineFilterManager.addFilters).not.toHaveBeenCalled(); }); + + it('should show warning if value type is unsupported', async () => { + await filterOutAction.execute({ + ...dataTableContext, + data: [ + { + ...context.data[0], + value: [{ test: 'value' }], + }, + ], + }); + expect(mockGlobalFilterManager.addFilters).not.toHaveBeenCalled(); + expect(mockTimelineFilterManager.addFilters).not.toHaveBeenCalled(); + expect(mockWarningToast).toHaveBeenCalled(); + }); }); describe('timeline scope execution', () => { diff --git a/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_out.ts b/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_out.ts index faa591b2c1617..38c2e52b7c7c7 100644 --- a/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_out.ts +++ b/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_out.ts @@ -6,6 +6,14 @@ */ import { addFilterIn, addFilterOut, createFilterOutActionFactory } from '@kbn/cell-actions'; +import { + isTypeSupportedByDefaultActions, + isValueSupportedByDefaultActions, + valueToArray, + filterOutNullableValues, +} from '@kbn/cell-actions/src/actions/utils'; +import { ACTION_INCOMPATIBLE_VALUE_WARNING } from '@kbn/cell-actions/src/actions/translations'; +import type { KBN_FIELD_TYPES } from '@kbn/field-types'; import { fieldHasCellActions } from '../../utils'; import type { SecurityAppStore } from '../../../common/store'; import type { StartServices } from '../../../types'; @@ -25,7 +33,12 @@ export const createFilterOutCellActionFactory = ({ const getTimelineById = timelineSelectors.getTimelineByIdSelector(); const { filterManager } = services.data.query; - const genericFilterOutActionFactory = createFilterOutActionFactory({ filterManager }); + const { notifications } = services; + + const genericFilterOutActionFactory = createFilterOutActionFactory({ + filterManager, + notifications, + }); return genericFilterOutActionFactory.combine({ type: SecurityCellActionType.FILTER, @@ -34,14 +47,24 @@ export const createFilterOutCellActionFactory = ({ return ( data.length === 1 && // TODO Add support for multiple values - fieldHasCellActions(field.name) + fieldHasCellActions(field.name) && + isTypeSupportedByDefaultActions(field.type as KBN_FIELD_TYPES) ); }, execute: async ({ data, metadata }) => { const field = data[0]?.field; - const value = data[0]?.value; + const rawValue = data[0]?.value; + const value = filterOutNullableValues(valueToArray(rawValue)); + + if (!isValueSupportedByDefaultActions(value)) { + notifications.toasts.addWarning({ + title: ACTION_INCOMPATIBLE_VALUE_WARNING, + }); + return; + } if (!field) return; + // if negateFilters is true we have to perform the opposite operation, we can just execute filterIn with the same params const addFilter = metadata?.negateFilters === true ? addFilterIn : addFilterOut; diff --git a/x-pack/plugins/security_solution/public/actions/filter/discover/filter_in.test.ts b/x-pack/plugins/security_solution/public/actions/filter/discover/filter_in.test.ts index aefe4698ed09e..a221210ce63d4 100644 --- a/x-pack/plugins/security_solution/public/actions/filter/discover/filter_in.test.ts +++ b/x-pack/plugins/security_solution/public/actions/filter/discover/filter_in.test.ts @@ -46,7 +46,7 @@ describe('createFilterInDiscoverCellActionFactory', () => { }); const context = { - data: [{ field: { name: 'user.name', type: 'text' }, value: 'the value' }], + data: [{ field: { name: 'user.name', type: 'string' }, value: 'the value' }], } as SecurityCellActionExecutionContext; it('should return display name', () => { diff --git a/x-pack/plugins/security_solution/public/actions/filter/discover/filter_out.test.ts b/x-pack/plugins/security_solution/public/actions/filter/discover/filter_out.test.ts index 42f6bad3040b8..bcbc4dcbbad39 100644 --- a/x-pack/plugins/security_solution/public/actions/filter/discover/filter_out.test.ts +++ b/x-pack/plugins/security_solution/public/actions/filter/discover/filter_out.test.ts @@ -48,7 +48,7 @@ describe('createFilterOutDiscoverCellActionFactory', () => { const context = { data: [ { - field: { name: 'user.name', type: 'text' }, + field: { name: 'user.name', type: 'string' }, value: 'the value', }, ], diff --git a/x-pack/plugins/security_solution/public/actions/show_top_n/cell_action/show_top_n.tsx b/x-pack/plugins/security_solution/public/actions/show_top_n/cell_action/show_top_n.tsx index cc7d8458e06c4..0e1c419b0449e 100644 --- a/x-pack/plugins/security_solution/public/actions/show_top_n/cell_action/show_top_n.tsx +++ b/x-pack/plugins/security_solution/public/actions/show_top_n/cell_action/show_top_n.tsx @@ -29,7 +29,7 @@ const SHOW_TOP = (fieldName: string) => }); const ICON = 'visBarVertical'; -const UNSUPPORTED_FIELD_TYPES = [ES_FIELD_TYPES.DATE, ES_FIELD_TYPES.TEXT]; +const UNSUPPORTED_ES_FIELD_TYPES = [ES_FIELD_TYPES.DATE, ES_FIELD_TYPES.TEXT]; export const createShowTopNCellActionFactory = createCellActionFactory( ({ @@ -52,7 +52,7 @@ export const createShowTopNCellActionFactory = createCellActionFactory( data.length === 1 && fieldHasCellActions(field.name) && (field.esTypes ?? []).every( - (esType) => !UNSUPPORTED_FIELD_TYPES.includes(esType as ES_FIELD_TYPES) + (esType) => !UNSUPPORTED_ES_FIELD_TYPES.includes(esType as ES_FIELD_TYPES) ) && !!field.aggregatable ); diff --git a/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_cell_actions.tsx b/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_cell_actions.tsx index 393232924ed3c..b6987f2fe29f6 100644 --- a/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_cell_actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_cell_actions.tsx @@ -72,7 +72,7 @@ export const getUseCellActionsHook = (tableId: TableId) => { const browserField: Partial | undefined = browserFieldsByName[column.id]; return { name: column.id, - type: browserField?.type ?? 'keyword', + type: browserField?.type ?? '', // When type is an empty string all cell actions are incompatible esTypes: browserField?.esTypes ?? [], aggregatable: browserField?.aggregatable ?? false, searchable: browserField?.searchable ?? false, diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/integration_tests/status_action.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/integration_tests/status_action.test.tsx index d229b297f239f..3888af95da704 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/integration_tests/status_action.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/integration_tests/status_action.test.tsx @@ -53,9 +53,10 @@ describe('When using processes action from response actions console', () => { }; const endpointDetailsMock = () => { + const newDate = new Date('2023-04-20T09:37:40.309Z'); const endpointMetadata = new EndpointMetadataGenerator('seed').generateHostInfo({ metadata: { - '@timestamp': new Date('2023-04-20T09:37:40.309Z').getTime(), + '@timestamp': newDate.getTime(), agent: { id: agentId, version: '8.8.0', @@ -69,6 +70,7 @@ describe('When using processes action from response actions console', () => { }, }, }, + last_checkin: newDate.toISOString(), }); useGetEndpointDetailsMock.mockReturnValue({ data: endpointMetadata, diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/status_action.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/status_action.tsx index e901e9b1a116d..357d0e566e328 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/status_action.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/status_action.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { memo, useEffect, useMemo, useCallback } from 'react'; +import React, { memo, useCallback, useEffect, useMemo } from 'react'; import { EuiDescriptionList } from '@elastic/eui'; import { v4 as uuidV4 } from 'uuid'; import { i18n } from '@kbn/i18n'; @@ -242,7 +242,7 @@ export const EndpointStatusActionResult = memo< 'xpack.securitySolution.endpointResponseActions.status.lastActive', { defaultMessage: 'Last active' } )} - value={endpointDetails.metadata['@timestamp']} + value={endpointDetails.last_checkin} /> ), diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_endpoint_info.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_endpoint_info.test.tsx index 4941fd59686cc..5a1c8bab4c05c 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_endpoint_info.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_endpoint_info.test.tsx @@ -58,7 +58,7 @@ describe('Responder header endpoint info', () => { ); expect(agentStatus.textContent).toBe(`UnhealthyIsolating`); }); - it('should show last updated time', async () => { + it('should show last checkin time', async () => { const lastUpdated = await renderResult.findByTestId('responderHeaderLastSeen'); expect(lastUpdated).toBeTruthy(); }); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_endpoint_info.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_endpoint_info.tsx index b56746e7890a6..e51989ce0cb7e 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_endpoint_info.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_endpoint_info.tsx @@ -9,10 +9,10 @@ import React, { memo } from 'react'; import { EuiFlexGroup, EuiFlexItem, - EuiText, EuiSkeletonText, - EuiToolTip, EuiSpacer, + EuiText, + EuiToolTip, } from '@elastic/eui'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { FormattedMessage, FormattedRelative } from '@kbn/i18n-react'; @@ -88,7 +88,7 @@ export const HeaderEndpointInfo = memo(({ endpointId }) id="xpack.securitySolution.responder.header.lastSeen" defaultMessage="Last seen {date}" values={{ - date: , + date: , }} /> diff --git a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoints_list.test.ts b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoints_list.test.ts index d6497c3516d82..d7f073b2a8338 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoints_list.test.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/endpoint/use_get_endpoints_list.test.ts @@ -142,6 +142,7 @@ describe('useGetEndpointsList hook', () => { : item.metadata.Endpoint.status, }, }, + last_checkin: item.last_checkin, }; }), }; @@ -164,9 +165,11 @@ describe('useGetEndpointsList hook', () => { const generator = new EndpointDocGenerator('seed'); const total = 60; const data = Array.from({ length: total }, () => { + const newDate = new Date(); const endpoint = { - metadata: generator.generateHostMetadata(), + metadata: generator.generateHostMetadata(newDate.getTime()), host_status: HostStatus.UNHEALTHY, + last_checkin: newDate.toISOString(), }; generator.updateCommonInfo(); @@ -200,9 +203,11 @@ describe('useGetEndpointsList hook', () => { const generator = new EndpointDocGenerator('seed'); const total = 61; const data = Array.from({ length: total }, () => { + const newDate = new Date(); const endpoint = { - metadata: generator.generateHostMetadata(), + metadata: generator.generateHostMetadata(newDate.getTime()), host_status: HostStatus.UNHEALTHY, + last_checkin: newDate.toISOString(), }; generator.updateCommonInfo(); @@ -229,7 +234,7 @@ describe('useGetEndpointsList hook', () => { .data.map((d) => d.metadata.agent.id) .slice(0, 50); - // call useGetEndpointsList with all 50 agents selected + // call useGetEndpointsList with all 50 agents selected const res = await renderReactQueryHook(() => useGetEndpointsList({ searchString: '', selectedAgentIds: agentIdsToSelect }) ); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/builders.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/builders.ts index e3b7bb29ba2b3..fb4270ee6dad5 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/builders.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/builders.ts @@ -19,11 +19,9 @@ export const initialEndpointPageState = (): Immutable => { loading: false, error: undefined, endpointDetails: { - hostDetails: { - details: undefined, - detailsLoading: false, - detailsError: undefined, - }, + hostInfo: undefined, + hostInfoError: undefined, + isHostInfoLoading: false, }, policyResponse: undefined, policyResponseLoading: false, diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts index f83b58f57fb12..1524b721cb07c 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts @@ -44,11 +44,9 @@ describe('EndpointList store concerns', () => { loading: false, error: undefined, endpointDetails: { - hostDetails: { - details: undefined, - detailsLoading: false, - detailsError: undefined, - }, + hostInfo: undefined, + hostInfoError: undefined, + isHostInfoLoading: false, }, policyResponse: undefined, policyResponseLoading: false, diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts index 580a3b761d245..d407a6cc27cce 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts @@ -16,22 +16,22 @@ import type { } from '@kbn/timelines-plugin/common'; import { BASE_POLICY_RESPONSE_ROUTE, + ENDPOINT_FIELDS_SEARCH_STRATEGY, HOST_METADATA_GET_ROUTE, HOST_METADATA_LIST_ROUTE, - metadataCurrentIndexPattern, - METADATA_UNITED_INDEX, METADATA_TRANSFORMS_STATUS_ROUTE, - ENDPOINT_FIELDS_SEARCH_STRATEGY, + METADATA_UNITED_INDEX, + metadataCurrentIndexPattern, } from '../../../../../common/endpoint/constants'; import type { GetHostPolicyResponse, HostInfo, HostIsolationRequestBody, - ResponseActionApiResponse, HostResultList, Immutable, ImmutableObject, MetadataListResponse, + ResponseActionApiResponse, } from '../../../../../common/endpoint/types'; import { isolateHost, unIsolateHost } from '../../../../common/lib/endpoint_isolation'; import { fetchPendingActionsByAgentId } from '../../../../common/lib/endpoint_pending_actions'; @@ -59,9 +59,9 @@ import type { } from '../types'; import type { EndpointPackageInfoStateChanged } from './action'; import { - detailsData, endpointPackageInfo, endpointPackageVersion, + fullDetailsHostInfo, getCurrentIsolationRequestState, getIsEndpointPackageInfoUninitialized, getIsIsolationRequestPending, @@ -86,7 +86,7 @@ export const endpointMiddlewareFactory: ImmutableMiddlewareFactory { // this needs to be called after endpointPackageVersion is loaded (getEndpointPackageInfo) - // or else wrong pattern might be loaded + // or else the wrong pattern might be loaded async function fetchIndexPatterns( state: ImmutableObject ): Promise { @@ -115,6 +115,7 @@ export const endpointMiddlewareFactory: ImmutableMiddlewareFactory (next) => async (action) => { next(action); @@ -329,13 +330,13 @@ const loadEndpointsPendingActions = async ({ dispatch, }: EndpointPageStore): Promise => { const state = getState(); - const detailsEndpoint = detailsData(state); + const detailsEndpoint = fullDetailsHostInfo(state); const listEndpoints = listData(state); const agentsIds = new Set(); // get all agent ids for the endpoints in the list if (detailsEndpoint) { - agentsIds.add(detailsEndpoint.elastic.agent.id); + agentsIds.add(detailsEndpoint.metadata.elastic.agent.id); } for (const endpointInfo of listEndpoints) { diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/mock_endpoint_result_list.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/mock_endpoint_result_list.ts index 671347dcd27b3..f6c5c144f529b 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/mock_endpoint_result_list.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/mock_endpoint_result_list.ts @@ -7,11 +7,11 @@ import type { HttpStart } from '@kbn/core/public'; import type { + BulkGetPackagePoliciesResponse, GetAgentPoliciesResponse, GetAgentPoliciesResponseItem, - GetPackagesResponse, GetAgentsResponse, - BulkGetPackagePoliciesResponse, + GetPackagesResponse, } from '@kbn/fleet-plugin/common/types/rest_spec'; import type { GetHostPolicyResponse, @@ -25,8 +25,8 @@ import { EndpointDocGenerator } from '../../../../../common/endpoint/generate_da import { INGEST_API_AGENT_POLICIES, INGEST_API_EPM_PACKAGES, - INGEST_API_PACKAGE_POLICIES, INGEST_API_FLEET_AGENTS, + INGEST_API_PACKAGE_POLICIES, } from '../../../services/policies/ingest'; import type { GetPolicyListResponse } from '../../policy/types'; import { pendingActionsResponseMock } from '../../../../common/lib/endpoint_pending_actions/mocks'; @@ -54,9 +54,12 @@ export const mockEndpointResultList: (options?: { const hosts: HostInfo[] = []; for (let index = 0; index < actualCountToReturn; index++) { + const newDate = new Date(); + const metadata = generator.generateHostMetadata(newDate.getTime()); hosts.push({ - metadata: generator.generateHostMetadata(), + metadata, host_status: HostStatus.UNHEALTHY, + last_checkin: newDate.toISOString(), }); } const mock: MetadataListResponse = { @@ -72,9 +75,12 @@ export const mockEndpointResultList: (options?: { * returns a mocked API response for retrieving a single host metadata */ export const mockEndpointDetailsApiResult = (): HostInfo => { + const newDate = new Date(); + const metadata = generator.generateHostMetadata(newDate.getTime()); return { - metadata: generator.generateHostMetadata(), + metadata, host_status: HostStatus.UNHEALTHY, + last_checkin: newDate.toISOString(), }; }; @@ -118,8 +124,8 @@ const endpointListApiPathHandlerMocks = ({ }; }, - // Do policies referenced in endpoint list exist - // just returns 1 single agent policy that includes all of the packagePolicy IDs provided + // Do policies reference in endpoint list exist + // just returns 1 single agent policy that includes all the packagePolicy IDs provided [INGEST_API_AGENT_POLICIES]: (): GetAgentPoliciesResponse => { return { items: [agentPolicy], @@ -184,7 +190,7 @@ const endpointListApiPathHandlerMocks = ({ }; /** - * Sets up mock impelementations in support of the Endpoints list view + * Sets up mock implementations in support of the Endpoints list view * * @param mockedHttpService * @param endpointsResults diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts index 8ad781c60dd20..db15ebcfa7343 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts @@ -11,11 +11,11 @@ import type { MetadataTransformStatsChanged, } from './action'; import { - isOnEndpointPage, + getCurrentIsolationRequestState, + getIsOnEndpointDetailsActivityLog, hasSelectedEndpoint, + isOnEndpointPage, uiQueryParams, - getIsOnEndpointDetailsActivityLog, - getCurrentIsolationRequestState, } from './selectors'; import type { EndpointState } from '../types'; import { initialEndpointPageState } from './builders'; @@ -97,7 +97,7 @@ export const endpointListReducer: StateReducer = (state = initialEndpointPageSta }, }; } else if (action.type === 'serverReturnedMetadataPatterns') { - // handle error case + // handle an error case return { ...state, patterns: action.payload, @@ -114,12 +114,8 @@ export const endpointListReducer: StateReducer = (state = initialEndpointPageSta endpointDetails: { ...state.endpointDetails, hostInfo: action.payload, - hostDetails: { - ...state.endpointDetails.hostDetails, - details: action.payload.metadata, - detailsLoading: false, - detailsError: undefined, - }, + hostInfoError: undefined, + isHostInfoLoading: false, }, policyVersionInfo: action.payload.policy_info, hostStatus: action.payload.host_status, @@ -129,11 +125,8 @@ export const endpointListReducer: StateReducer = (state = initialEndpointPageSta ...state, endpointDetails: { ...state.endpointDetails, - hostDetails: { - ...state.endpointDetails.hostDetails, - detailsError: action.payload, - detailsLoading: false, - }, + hostInfoError: action.payload, + isHostInfoLoading: false, }, }; } else if (action.type === 'endpointPendingActionsStateChanged') { @@ -262,44 +255,35 @@ export const endpointListReducer: StateReducer = (state = initialEndpointPageSta ...stateUpdates, endpointDetails: { ...state.endpointDetails, - hostDetails: { - ...state.endpointDetails.hostDetails, - detailsError: undefined, - }, + hostInfoError: undefined, }, loading: true, policyItemsLoading: true, }; } } else if (isCurrentlyOnDetailsPage) { - // if previous page was the list or another endpoint details page, load endpoint details only + // if the previous page was the list or another endpoint details page, load endpoint details only if (wasPreviouslyOnDetailsPage || wasPreviouslyOnListPage) { return { ...state, ...stateUpdates, endpointDetails: { ...state.endpointDetails, - hostDetails: { - ...state.endpointDetails.hostDetails, - detailsLoading: !isNotLoadingDetails, - detailsError: undefined, - }, + hostInfoError: undefined, + isHostInfoLoading: !isNotLoadingDetails, }, detailsLoading: true, policyResponseLoading: true, }; } else { - // if previous page was not endpoint list or endpoint details, load both list and details + // if the previous page was not endpoint list or endpoint details, load both list and details return { ...state, ...stateUpdates, endpointDetails: { ...state.endpointDetails, - hostDetails: { - ...state.endpointDetails.hostDetails, - detailsLoading: true, - detailsError: undefined, - }, + hostInfoError: undefined, + isHostInfoLoading: true, }, loading: true, policyResponseLoading: true, @@ -307,16 +291,13 @@ export const endpointListReducer: StateReducer = (state = initialEndpointPageSta }; } } - // otherwise we are not on a endpoint list or details page + // otherwise, we are not on an endpoint list or details page return { ...state, ...stateUpdates, endpointDetails: { ...state.endpointDetails, - hostDetails: { - ...state.endpointDetails.hostDetails, - detailsError: undefined, - }, + hostInfoError: undefined, }, endpointsExist: true, }; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts index 1b4c716c37462..c01a7ea65a8d6 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts @@ -11,9 +11,9 @@ import { createSelector } from 'reselect'; import { matchPath } from 'react-router-dom'; import { decode } from '@kbn/rison'; import type { Query } from '@kbn/es-query'; -import type { Immutable, EndpointPendingActions } from '../../../../../common/endpoint/types'; +import type { EndpointPendingActions, Immutable } from '../../../../../common/endpoint/types'; import { HostStatus } from '../../../../../common/endpoint/types'; -import type { EndpointState, EndpointIndexUIQueryParams } from '../types'; +import type { EndpointIndexUIQueryParams, EndpointState } from '../types'; import { extractListPaginationParams } from '../../../common/routing'; import { MANAGEMENT_DEFAULT_PAGE, @@ -43,26 +43,23 @@ export const listLoading = (state: Immutable): boolean => state.l export const listError = (state: Immutable) => state.error; -export const detailsData = (state: Immutable) => - state.endpointDetails.hostDetails.details; - -export const fullDetailsHostInfo = (state: Immutable) => - state.endpointDetails.hostInfo; +export const fullDetailsHostInfo = ( + state: Immutable +): EndpointState['endpointDetails']['hostInfo'] => state.endpointDetails.hostInfo; -export const detailsLoading = (state: Immutable): boolean => - state.endpointDetails.hostDetails.detailsLoading; +export const isHostInfoLoading = ( + state: Immutable +): EndpointState['endpointDetails']['isHostInfoLoading'] => state.endpointDetails.isHostInfoLoading; -export const detailsError = ( +export const hostInfoError = ( state: Immutable -): EndpointState['endpointDetails']['hostDetails']['detailsError'] => - state.endpointDetails.hostDetails.detailsError; +): EndpointState['endpointDetails']['hostInfoError'] => state.endpointDetails.hostInfoError; export const policyItems = (state: Immutable) => state.policyItems; export const policyItemsLoading = (state: Immutable) => state.policyItemsLoading; export const selectedPolicyId = (state: Immutable) => state.selectedPolicyId; - export const endpointPackageInfo = (state: Immutable) => state.endpointPackageInfo; export const getIsEndpointPackageInfoUninitialized: (state: Immutable) => boolean = createSelector(endpointPackageInfo, (packageInfo) => isUninitialisedResourceState(packageInfo)); @@ -258,8 +255,8 @@ export const getIsOnEndpointDetailsActivityLog: (state: Immutable return searchParams.show === EndpointDetailsTabsTypes.activityLog; }); -export const getIsEndpointHostIsolated = createSelector(detailsData, (details) => { - return (details && isEndpointHostIsolated(details)) || false; +export const getIsEndpointHostIsolated = createSelector(fullDetailsHostInfo, (details) => { + return (details && isEndpointHostIsolated(details.metadata)) || false; }); export const getEndpointPendingActionsState = ( diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts index cdd5020226697..c7de43f6bc374 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts @@ -8,15 +8,14 @@ import type { DataViewBase } from '@kbn/es-query'; import type { GetInfoResponse } from '@kbn/fleet-plugin/common'; import type { + AppLocation, + EndpointPendingActions, HostInfo, - Immutable, - HostMetadata, HostPolicyResponse, - AppLocation, - PolicyData, HostStatus, + Immutable, + PolicyData, ResponseActionApiResponse, - EndpointPendingActions, } from '../../../../common/endpoint/types'; import type { ServerApiError } from '../../../common/types'; import type { AsyncResourceState } from '../../state'; @@ -39,14 +38,8 @@ export interface EndpointState { // Adding `hostInfo` to store full API response in order to support the // refactoring effort with AgentStatus component hostInfo?: HostInfo; - hostDetails: { - /** details data for a specific host */ - details?: Immutable; - /** details page is retrieving data */ - detailsLoading: boolean; - /** api error from retrieving host details */ - detailsError?: ServerApiError; - }; + hostInfoError?: ServerApiError; + isHostInfoLoading: boolean; }; /** Holds the Policy Response for the Host currently being displayed in the details */ policyResponse?: HostPolicyResponse; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.tsx index 69a4dd2054e6c..7942b10059bcb 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.tsx @@ -9,12 +9,12 @@ import React, { useState, useCallback, useMemo } from 'react'; import { EuiContextMenuPanel, EuiButton, EuiPopover } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { useEndpointActionItems, useEndpointSelector } from '../../hooks'; -import { detailsData } from '../../../store/selectors'; +import { fullDetailsHostInfo } from '../../../store/selectors'; import { ContextMenuItemNavByRouter } from '../../../../../components/context_menu_with_router_support/context_menu_item_nav_by_router'; export const ActionsMenu = React.memo<{}>(() => { - const endpointDetails = useEndpointSelector(detailsData); - const menuOptions = useEndpointActionItems(endpointDetails); + const endpointDetails = useEndpointSelector(fullDetailsHostInfo); + const menuOptions = useEndpointActionItems(endpointDetails?.metadata); const [isPopoverOpen, setIsPopoverOpen] = useState(false); const closePopoverHandler = useCallback(() => { diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/flyout_header.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/flyout_header.tsx index 8cc41f19e94a3..256b606ad5110 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/flyout_header.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/flyout_header.tsx @@ -6,9 +6,9 @@ */ import React, { memo } from 'react'; -import { EuiFlyoutHeader, EuiSkeletonText, EuiToolTip, EuiTitle } from '@elastic/eui'; +import { EuiFlyoutHeader, EuiSkeletonText, EuiTitle, EuiToolTip } from '@elastic/eui'; import { useEndpointSelector } from '../../hooks'; -import { detailsLoading } from '../../../store/selectors'; +import { isHostInfoLoading } from '../../../store/selectors'; import { BackToEndpointDetailsFlyoutSubHeader } from './back_to_endpoint_details_flyout_subheader'; export const EndpointDetailsFlyoutHeader = memo( @@ -21,9 +21,9 @@ export const EndpointDetailsFlyoutHeader = memo( endpointId?: string; hasBorder?: boolean; hostname?: string; - children?: React.ReactNode | React.ReactNodeArray; + children?: React.ReactNode | React.ReactNode[]; }) => { - const hostDetailsLoading = useEndpointSelector(detailsLoading); + const hostDetailsLoading = useEndpointSelector(isHostInfoLoading); return ( 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 b52aaa0da4223..a7f02fbb5da03 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 @@ -14,9 +14,8 @@ import type { HostMetadata } from '../../../../../../common/endpoint/types'; import { useToasts } from '../../../../../common/lib/kibana'; import { getEndpointDetailsPath } from '../../../../common/routing'; import { - detailsData, - detailsError, - hostStatusInfo, + fullDetailsHostInfo, + hostInfoError, policyVersionInfo, showView, uiQueryParams, @@ -26,8 +25,8 @@ import * as i18 from '../translations'; import { ActionsMenu } from './components/actions_menu'; import { EndpointDetailsFlyoutTabs, - EndpointDetailsTabsTypes, type EndpointDetailsTabs, + EndpointDetailsTabsTypes, } from './components/endpoint_details_tabs'; import { EndpointIsolationFlyoutPanel } from './components/endpoint_isolate_flyout_panel'; import { EndpointDetailsFlyoutHeader } from './components/flyout_header'; @@ -37,11 +36,10 @@ export const EndpointDetails = memo(() => { const toasts = useToasts(); const queryParams = useEndpointSelector(uiQueryParams); - const hostDetails = useEndpointSelector(detailsData); - const hostDetailsError = useEndpointSelector(detailsError); + const hostInfo = useEndpointSelector(fullDetailsHostInfo); + const hostDetailsError = useEndpointSelector(hostInfoError); const policyInfo = useEndpointSelector(policyVersionInfo); - const hostStatus = useEndpointSelector(hostStatusInfo); const show = useEndpointSelector(showView); const { canAccessEndpointActionsLogManagement } = useUserPrivileges().endpointPrivileges; @@ -68,14 +66,10 @@ export const EndpointDetails = memo(() => { selected_endpoint: id, }), content: - hostDetails === undefined ? ( + hostInfo === undefined ? ( ContentLoadingMarkup ) : ( - + ), }, ]; @@ -96,14 +90,7 @@ export const EndpointDetails = memo(() => { } return tabs; }, - [ - canAccessEndpointActionsLogManagement, - ContentLoadingMarkup, - hostDetails, - policyInfo, - hostStatus, - queryParams, - ] + [canAccessEndpointActionsLogManagement, ContentLoadingMarkup, hostInfo, policyInfo, queryParams] ); const showFlyoutFooter = @@ -127,11 +114,11 @@ export const EndpointDetails = memo(() => { {(show === 'policy_response' || show === 'isolate' || show === 'unisolate') && ( )} - {hostDetails === undefined ? ( + {hostInfo === undefined ? ( @@ -139,18 +126,18 @@ export const EndpointDetails = memo(() => { <> {(show === 'details' || show === 'activity_log') && ( )} - {show === 'policy_response' && } + {show === 'policy_response' && } {(show === 'isolate' || show === 'unisolate') && ( - + )} {showFlyoutFooter && ( diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details_content.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details_content.tsx index b33f98078b9fb..13878c9377eb4 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details_content.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details_content.tsx @@ -8,21 +8,20 @@ import styled from 'styled-components'; import { EuiDescriptionList, - EuiText, EuiFlexGroup, EuiFlexItem, - EuiSpacer, - EuiLink, EuiHealth, + EuiLink, + EuiSpacer, + EuiText, } from '@elastic/eui'; import React, { memo, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EndpointAgentStatus } from '../../../../../common/components/endpoint/endpoint_agent_status'; import { isPolicyOutOfDate } from '../../utils'; -import type { HostInfo, HostMetadata, HostStatus } from '../../../../../../common/endpoint/types'; +import type { HostInfo } from '../../../../../../common/endpoint/types'; import { useEndpointSelector } from '../hooks'; import { - fullDetailsHostInfo, getEndpointPendingActionsCallback, nonExistingPolicies, policyResponseStatus, @@ -39,9 +38,11 @@ const EndpointDetailsContentStyled = styled.div` dl dt { max-width: 27%; } + dl dd { max-width: 73%; } + .policyLineText { padding-right: 5px; } @@ -55,34 +56,28 @@ const ColumnTitle = ({ children }: { children: React.ReactNode }) => { ); }; -export const EndpointDetailsContent = memo( - ({ - details, - policyInfo, - hostStatus, - }: { - details: HostMetadata; - policyInfo?: HostInfo['policy_info']; - hostStatus: HostStatus; - }) => { +interface EndpointDetailsContentProps { + hostInfo: HostInfo; + policyInfo?: HostInfo['policy_info']; +} + +export const EndpointDetailsContent = memo( + ({ hostInfo, policyInfo }) => { const queryParams = useEndpointSelector(uiQueryParams); const policyStatus = useEndpointSelector( policyResponseStatus ) as keyof typeof POLICY_STATUS_TO_BADGE_COLOR; const getHostPendingActions = useEndpointSelector(getEndpointPendingActionsCallback); const missingPolicies = useEndpointSelector(nonExistingPolicies); - const hostInfo = useEndpointSelector(fullDetailsHostInfo); const policyResponseRoutePath = useMemo(() => { - // eslint-disable-next-line @typescript-eslint/naming-convention - const { selected_endpoint, show, ...currentUrlParams } = queryParams; - const path = getEndpointDetailsPath({ + const { selected_endpoint: selectedEndpoint, show, ...currentUrlParams } = queryParams; + return getEndpointDetailsPath({ name: 'endpointPolicyResponse', ...currentUrlParams, - selected_endpoint: details.agent.id, + selected_endpoint: hostInfo.metadata.agent.id, }); - return path; - }, [details.agent.id, queryParams]); + }, [hostInfo.metadata.agent.id, queryParams]); const policyStatusClickHandler = useNavigateByRouterEventHandler(policyResponseRoutePath); @@ -97,7 +92,7 @@ export const EndpointDetailsContent = memo( /> ), - description: {details.host.os.full}, + description: {hostInfo.metadata.host.os.full}, }, { title: ( @@ -108,13 +103,11 @@ export const EndpointDetailsContent = memo( /> ), - description: hostInfo ? ( + description: ( - ) : ( - <> ), }, { @@ -128,7 +121,10 @@ export const EndpointDetailsContent = memo( ), description: ( - + ), }, @@ -144,14 +140,14 @@ export const EndpointDetailsContent = memo( description: ( - {details.Endpoint.policy.applied.name} + {hostInfo.metadata.Endpoint.policy.applied.name} - {details.Endpoint.policy.applied.endpoint_policy_version && ( + {hostInfo.metadata.Endpoint.policy.applied.endpoint_policy_version && ( )} - {isPolicyOutOfDate(details.Endpoint.policy.applied, policyInfo) && } + {isPolicyOutOfDate(hostInfo.metadata.Endpoint.policy.applied, policyInfo) && ( + + )} ), }, @@ -206,7 +204,7 @@ export const EndpointDetailsContent = memo( /> ), - description: {details.agent.version}, + description: {hostInfo.metadata.agent.version}, }, { title: ( @@ -219,7 +217,7 @@ export const EndpointDetailsContent = memo( ), description: ( - {details.host.ip.map((ip: string, index: number) => ( + {hostInfo.metadata.host.ip.map((ip: string, index: number) => ( {ip} @@ -229,9 +227,8 @@ export const EndpointDetailsContent = memo( }, ]; }, [ - details, - getHostPendingActions, hostInfo, + getHostPendingActions, missingPolicies, policyInfo, policyStatus, diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx index 9b2e681e4f4be..027f2cc20780d 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx @@ -48,8 +48,8 @@ import { import type { TransformStats } from '../types'; import { HOST_METADATA_LIST_ROUTE, - metadataTransformPrefix, METADATA_UNITED_TRANSFORM, + metadataTransformPrefix, } from '../../../../../common/endpoint/constants'; import { useUserPrivileges } from '../../../../common/components/user_privileges'; import { @@ -62,7 +62,7 @@ import { getEndpointPrivilegesInitialStateMock } from '../../../../common/compon const mockUserPrivileges = useUserPrivileges as jest.Mock; // not sure why this can't be imported from '../../../../common/mock/formatted_relative'; -// but sure enough it needs to be inline in this one file +// but sure enough, it needs to be inline in this one file jest.mock('@kbn/i18n-react', () => { const originalModule = jest.requireActual('@kbn/i18n-react'); const FormattedRelative = jest.fn().mockImplementation(() => '20 hours ago'); @@ -310,6 +310,7 @@ describe('when on the endpoint list page', () => { hostListData[index].metadata.Endpoint.policy.applied, setup.policy ), + last_checkin: hostListData[index].last_checkin, }; }); hostListData.forEach((item, index) => { @@ -485,17 +486,17 @@ describe('when on the endpoint list page', () => { }); describe('when there is a selected host in the url', () => { - let hostDetails: HostInfo; + let hostInfo: HostInfo; let renderAndWaitForData: () => Promise>; const mockEndpointListApi = (mockedPolicyResponse?: HostPolicyResponse) => { const { - // eslint-disable-next-line @typescript-eslint/naming-convention - host_status, + host_status: hostStatus, + last_checkin: lastCheckin, metadata: { agent, Endpoint, ...details }, } = mockEndpointDetailsApiResult(); - hostDetails = { - host_status, + hostInfo = { + host_status: hostStatus, metadata: { ...details, Endpoint: { @@ -510,13 +511,14 @@ describe('when on the endpoint list page', () => { id: '1', }, }, + last_checkin: lastCheckin, }; const policy = docGenerator.generatePolicyPackagePolicy(); - policy.id = hostDetails.metadata.Endpoint.policy.applied.id; + policy.id = hostInfo.metadata.Endpoint.policy.applied.id; setEndpointListApiMockImplementation(coreStart.http, { - endpointsResults: [hostDetails], + endpointsResults: [hostInfo], endpointPackagePolicies: [policy], policyResponse: mockedPolicyResponse, }); @@ -617,7 +619,7 @@ describe('when on the endpoint list page', () => { const policyDetailsLink = await renderResult.findByTestId('policyDetailsValue'); expect(policyDetailsLink).not.toBeNull(); expect(policyDetailsLink.getAttribute('href')).toEqual( - `${APP_PATH}${MANAGEMENT_PATH}/policy/${hostDetails.metadata.Endpoint.policy.applied.id}/settings` + `${APP_PATH}${MANAGEMENT_PATH}/policy/${hostInfo.metadata.Endpoint.policy.applied.id}/settings` ); }); @@ -626,7 +628,7 @@ describe('when on the endpoint list page', () => { const policyDetailsRevElement = await renderResult.findByTestId('policyDetailsRevNo'); expect(policyDetailsRevElement).not.toBeNull(); expect(policyDetailsRevElement.textContent).toEqual( - `rev. ${hostDetails.metadata.Endpoint.policy.applied.endpoint_policy_version}` + `rev. ${hostInfo.metadata.Endpoint.policy.applied.endpoint_policy_version}` ); }); @@ -639,7 +641,7 @@ describe('when on the endpoint list page', () => { }); const changedUrlAction = await userChangedUrlChecker; expect(changedUrlAction.payload.pathname).toEqual( - `${MANAGEMENT_PATH}/policy/${hostDetails.metadata.Endpoint.policy.applied.id}/settings` + `${MANAGEMENT_PATH}/policy/${hostInfo.metadata.Endpoint.policy.applied.id}/settings` ); }); @@ -1019,6 +1021,7 @@ describe('when on the endpoint list page', () => { version: '7.14.0', }, }, + last_checkin: hosts[0].last_checkin, }, { host_status: hosts[1].host_status, @@ -1044,6 +1047,7 @@ describe('when on the endpoint list page', () => { version: '8.4.0', }, }, + last_checkin: hosts[1].last_checkin, }, ]; @@ -1333,7 +1337,7 @@ describe('when on the endpoint list page', () => { beforeEach(async () => { const { data: hosts } = mockEndpointResultList({ total: 2 }); - // second host is isolated, for unisolate testing + // the second host is isolated, for unisolate testing const hostInfo: HostInfo[] = [ { host_status: hosts[0].host_status, @@ -1359,6 +1363,7 @@ describe('when on the endpoint list page', () => { version: '7.14.0', }, }, + last_checkin: hosts[0].last_checkin, }, { host_status: hosts[1].host_status, @@ -1384,6 +1389,7 @@ describe('when on the endpoint list page', () => { version: '8.4.0', }, }, + last_checkin: hosts[1].last_checkin, }, ]; setEndpointListApiMockImplementation(coreStart.http, { diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/response_actions.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/response_actions.ts index f8c3aad63f4e2..8a5c77c2a61b0 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/response_actions.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/response_actions.ts @@ -209,8 +209,9 @@ export const sendEndpointActionResponse = async ( const fileMeta = await esClient.index({ index: FILE_STORAGE_METADATA_INDEX, id: getFileDownloadId(action, action.agents[0]), - body: fileMetaDoc, + op_type: 'create', refresh: 'wait_for', + body: fileMetaDoc, }); // Index the file content (just one chunk) @@ -224,12 +225,14 @@ export const sendEndpointActionResponse = async ( document: cborx.encode({ bid: fileMeta._id, last: true, + '@timestamp': new Date().toISOString(), data: Buffer.from( 'UEsDBAoACQAAAFZeRFWpAsDLHwAAABMAAAAMABwAYmFkX2ZpbGUudHh0VVQJAANTVjxjU1Y8Y3V4CwABBPUBAAAEFAAAAMOcoyEq/Q4VyG02U9O0LRbGlwP/y5SOCfRKqLz1rsBQSwcIqQLAyx8AAAATAAAAUEsBAh4DCgAJAAAAVl5EVakCwMsfAAAAEwAAAAwAGAAAAAAAAQAAAKSBAAAAAGJhZF9maWxlLnR4dFVUBQADU1Y8Y3V4CwABBPUBAAAEFAAAAFBLBQYAAAAAAQABAFIAAAB1AAAAAAA=', 'base64' ), }), refresh: 'wait_for', + op_type: 'create', }, { headers: { diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/stack_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/stack_services.ts index 434df1b209a19..e38725e5d5e6e 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/stack_services.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/stack_services.ts @@ -44,7 +44,7 @@ export interface RuntimeServices { interface CreateRuntimeServicesOptions { kibanaUrl: string; elasticsearchUrl: string; - fleetServerUrl: string | undefined; + fleetServerUrl?: string; username: string; password: string; log?: ToolingLog; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/mocks.ts index d1818b9b494e7..a4536f54b92d7 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/mocks.ts @@ -214,6 +214,7 @@ export const generateFileMetadataDocumentMock = ( transithash: { sha256: 'a0d6d6a2bb73340d4a0ed32b2a46272a19dd111427770c072918aed7a8565010', }, + '@timestamp': new Date().toISOString(), ...overrides, }; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.test.ts index 27c758ca43a8b..ebe8a34580f76 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.test.ts @@ -15,8 +15,8 @@ import { } from '../../routes/metadata/support/test_support'; import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data'; import { - getESQueryHostMetadataByFleetAgentIds, buildUnitedIndexQuery, + getESQueryHostMetadataByFleetAgentIds, } from '../../routes/metadata/query_builders'; import type { HostMetadata } from '../../../../common/endpoint/types'; import type { Agent, PackagePolicy } from '@kbn/fleet-plugin/common'; @@ -137,11 +137,14 @@ describe('EndpointMetadataService', () => { package_policies: packagePolicies, }), ]; + + const newDate = new Date(); const agentPolicyIds = agentPolicies.map((policy) => policy.id); - const endpointMetadataDoc = endpointDocGenerator.generateHostMetadata(); + const endpointMetadataDoc = endpointDocGenerator.generateHostMetadata(newDate.getTime()); const mockAgent = { policy_id: agentPolicies[0].id, policy_revision: agentPolicies[0].revision, + last_checkin: newDate.toISOString(), } as unknown as Agent; const mockDoc = unitedMetadataSearchResponseMock(endpointMetadataDoc, mockAgent); esClient.search.mockResponse(mockDoc); @@ -203,6 +206,7 @@ describe('EndpointMetadataService', () => { revision: packagePolicies[0].revision, }, }, + last_checkin: newDate.toISOString(), }, ], total: 1, diff --git a/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.ts b/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.ts index 98f42c1a4d8ce..d6247ad1b572b 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.ts @@ -12,7 +12,7 @@ import type { SavedObjectsServiceStart, } from '@kbn/core/server'; -import type { SearchTotalHits, SearchResponse } from '@elastic/elasticsearch/lib/api/types'; +import type { SearchResponse, SearchTotalHits } from '@elastic/elasticsearch/lib/api/types'; import type { Agent, AgentPolicy, AgentStatus, PackagePolicy } from '@kbn/fleet-plugin/common'; import type { AgentPolicyServiceInterface, PackagePolicyClient } from '@kbn/fleet-plugin/server'; import { AgentNotFoundError } from '@kbn/fleet-plugin/server'; @@ -32,9 +32,9 @@ import { FleetEndpointPackagePolicyNotFoundError, } from './errors'; import { + buildUnitedIndexQuery, getESQueryHostMetadataByFleetAgentIds, getESQueryHostMetadataByID, - buildUnitedIndexQuery, getESQueryHostMetadataByIDs, } from '../../routes/metadata/query_builders'; import { @@ -176,7 +176,7 @@ export class EndpointMetadataService { } } - // If the agent is not longer active, then that means that the Agent/Endpoint have been un-enrolled from the host + // If the agent is no longer active, then that means that the Agent/Endpoint have been un-enrolled from the host if (fleetAgent && !fleetAgent.active) { throw new EndpointHostUnEnrolledError( `Endpoint with id ${endpointId} (Fleet agent id ${fleetAgentId}) is unenrolled` @@ -251,7 +251,7 @@ export class EndpointMetadataService { } } - // The fleetAgentPolicy might have the endpoint policy in the `package_policies`, lets check that first + // The fleetAgentPolicy might have the endpoint policy in the `package_policies`, let's check that first if ( !endpointPackagePolicy && fleetAgentPolicy && @@ -262,7 +262,7 @@ export class EndpointMetadataService { ); } - // if we still don't have an endpoint package policy, try retrieving it from fleet + // if we still don't have an endpoint package policy, try retrieving it from `fleet` if (!endpointPackagePolicy) { try { endpointPackagePolicy = await this.getFleetEndpointPackagePolicy( @@ -294,6 +294,8 @@ export class EndpointMetadataService { id: endpointPackagePolicy?.id ?? '', }, }, + last_checkin: + _fleetAgent?.last_checkin || new Date(endpointMetadata['@timestamp']).toISOString(), }; } @@ -363,6 +365,8 @@ export class EndpointMetadataService { * * @param esClient * @param queryOptions + * @param soClient + * @param fleetServices * * @throws */ diff --git a/x-pack/plugins/serverless_observability/kibana.jsonc b/x-pack/plugins/serverless_observability/kibana.jsonc index af3fca8d81938..0f85a1033c2ea 100644 --- a/x-pack/plugins/serverless_observability/kibana.jsonc +++ b/x-pack/plugins/serverless_observability/kibana.jsonc @@ -8,7 +8,7 @@ "server": true, "browser": true, "configPath": ["xpack", "serverless", "observability"], - "requiredPlugins": ["serverless", "observabilityShared", "kibanaReact", "management"], + "requiredPlugins": ["serverless", "observabilityShared", "kibanaReact", "management", "ml"], "optionalPlugins": [], "requiredBundles": [] } diff --git a/x-pack/plugins/serverless_observability/public/components/side_navigation/index.tsx b/x-pack/plugins/serverless_observability/public/components/side_navigation/index.tsx index 23987f7d86e95..70ba2b98d5707 100644 --- a/x-pack/plugins/serverless_observability/public/components/side_navigation/index.tsx +++ b/x-pack/plugins/serverless_observability/public/components/side_navigation/index.tsx @@ -11,7 +11,6 @@ import { DefaultNavigation, NavigationKibanaProvider, NavigationTreeDefinition, - getPresets, } from '@kbn/shared-ux-chrome-navigation'; import React from 'react'; import { i18n } from '@kbn/i18n'; @@ -28,7 +27,7 @@ const navigationTree: NavigationTreeDefinition = { breadcrumbStatus: 'hidden', children: [ { - id: 'discover-dashboard-viz', + id: 'discover-dashboard-alerts-slos', children: [ { link: 'discover', @@ -39,44 +38,75 @@ const navigationTree: NavigationTreeDefinition = { }), link: 'dashboards', }, - { - title: i18n.translate('xpack.serverlessObservability.nav.visualizations', { - defaultMessage: 'Visualizations', - }), - link: 'visualize', - }, - ], - }, - { - id: 'alerts-cases-slos', - children: [ { link: 'observability-overview:alerts', }, { - link: 'observability-overview:cases', + link: 'observability-overview:slos', }, { - link: 'observability-overview:slos', + id: 'aiops', + title: 'AIOps', + children: [ + { + title: i18n.translate('xpack.serverlessObservability.nav.ml.jobs', { + defaultMessage: 'Anomaly detection', + }), + link: 'ml:anomalyDetection', + }, + { + title: i18n.translate('xpack.serverlessObservability.ml.spike.analysis', { + defaultMessage: 'Spike analysis', + }), + link: 'ml:explainLogRateSpikes', + icon: 'beaker', + }, + { + link: 'ml:changePointDetections', + icon: 'beaker', + }, + { + title: i18n.translate('xpack.serverlessObservability.nav.ml.job.notifications', { + defaultMessage: 'Job notifications', + }), + link: 'ml:notifications', + }, + ], }, ], }, + { - id: 'apm', - title: 'APM', + id: 'applications', children: [ - { link: 'apm:services' }, { - link: 'apm:traces', + id: 'apm', + title: 'Applications', + children: [ + { + link: 'apm:services', + }, + { + link: 'apm:traces', + }, + { + link: 'apm:dependencies', + }, + ], }, + ], + }, + { + id: 'cases-vis', + children: [ { - title: i18n.translate('xpack.serverlessObservability.nav.logs', { - defaultMessage: 'Logs', - }), - link: 'logs:stream', + link: 'observability-overview:cases', }, { - link: 'apm:dependencies', + title: i18n.translate('xpack.serverlessObservability.nav.visualizations', { + defaultMessage: 'Visualizations', + }), + link: 'visualize', }, ], }, @@ -85,9 +115,8 @@ const navigationTree: NavigationTreeDefinition = { children: [ { title: i18n.translate('xpack.serverlessObservability.nav.getStarted', { - defaultMessage: 'Get started', + defaultMessage: 'Add data', }), - icon: 'launch', link: 'observabilityOnboarding', }, ], @@ -98,7 +127,30 @@ const navigationTree: NavigationTreeDefinition = { footer: [ { type: 'navGroup', - ...getPresets('management'), + id: 'projest_settings_project_nav', + title: 'Project settings', + icon: 'gear', + defaultIsCollapsed: true, + breadcrumbStatus: 'hidden', + children: [ + { + id: 'settings', + children: [ + { + link: 'management', + title: i18n.translate('xpack.serverlessObservability.nav.mngt', { + defaultMessage: 'Management', + }), + }, + { + link: 'integrations', + }, + { + link: 'fleet', + }, + ], + }, + ], }, ], }; diff --git a/x-pack/plugins/serverless_observability/server/plugin.ts b/x-pack/plugins/serverless_observability/server/plugin.ts index 8b28ba2b0a4ac..ae7bcd8baa064 100644 --- a/x-pack/plugins/serverless_observability/server/plugin.ts +++ b/x-pack/plugins/serverless_observability/server/plugin.ts @@ -5,16 +5,28 @@ * 2.0. */ -import { PluginInitializerContext, Plugin } from '@kbn/core/server'; +import type { PluginInitializerContext, Plugin, CoreSetup } from '@kbn/core/server'; -import { ServerlessObservabilityPluginSetup, ServerlessObservabilityPluginStart } from './types'; +import type { + ServerlessObservabilityPluginSetup, + ServerlessObservabilityPluginStart, + SetupDependencies, + StartDependencies, +} from './types'; export class ServerlessObservabilityPlugin - implements Plugin + implements + Plugin< + ServerlessObservabilityPluginSetup, + ServerlessObservabilityPluginStart, + SetupDependencies, + StartDependencies + > { constructor(_initializerContext: PluginInitializerContext) {} - public setup() { + public setup(_coreSetup: CoreSetup, pluginsSetup: SetupDependencies) { + pluginsSetup.ml.setFeaturesEnabled({ ad: true, dfa: false, nlp: false }); return {}; } diff --git a/x-pack/plugins/serverless_observability/server/types.ts b/x-pack/plugins/serverless_observability/server/types.ts index f8a587103e886..5ebad2274b9a5 100644 --- a/x-pack/plugins/serverless_observability/server/types.ts +++ b/x-pack/plugins/serverless_observability/server/types.ts @@ -5,7 +5,16 @@ * 2.0. */ +import type { MlPluginSetup } from '@kbn/ml-plugin/server'; + // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface ServerlessObservabilityPluginSetup {} // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface ServerlessObservabilityPluginStart {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface StartDependencies {} + +export interface SetupDependencies { + ml: MlPluginSetup; +} diff --git a/x-pack/plugins/serverless_observability/tsconfig.json b/x-pack/plugins/serverless_observability/tsconfig.json index 5617f68278193..c45e5484f3ca3 100644 --- a/x-pack/plugins/serverless_observability/tsconfig.json +++ b/x-pack/plugins/serverless_observability/tsconfig.json @@ -22,6 +22,7 @@ "@kbn/observability-shared-plugin", "@kbn/kibana-react-plugin", "@kbn/shared-ux-chrome-navigation", + "@kbn/ml-plugin", "@kbn/i18n", "@kbn/management-cards-navigation", ] diff --git a/x-pack/plugins/serverless_search/kibana.jsonc b/x-pack/plugins/serverless_search/kibana.jsonc index 95484ec54dc43..a8c6b8c3b77ee 100644 --- a/x-pack/plugins/serverless_search/kibana.jsonc +++ b/x-pack/plugins/serverless_search/kibana.jsonc @@ -9,18 +9,19 @@ "browser": true, "configPath": ["xpack", "serverless", "search"], "requiredPlugins": [ - "serverless", "cloud", - "management", - "security", - "share", - "devTools", "console", - "searchprofiler", + "dashboard", + "devTools", + "discover", "grokdebugger", + "management", + "ml", "painlessLab", - "discover", - "dashboard", + "searchprofiler", + "security", + "serverless", + "share", "visualizations" ], "optionalPlugins": [], diff --git a/x-pack/plugins/serverless_search/public/layout/nav.tsx b/x-pack/plugins/serverless_search/public/layout/nav.tsx index b1576c539a649..afc8aba90cbc8 100644 --- a/x-pack/plugins/serverless_search/public/layout/nav.tsx +++ b/x-pack/plugins/serverless_search/public/layout/nav.tsx @@ -5,16 +5,16 @@ * 2.0. */ -import { CoreStart } from '@kbn/core/public'; +import type { CoreStart } from '@kbn/core/public'; import { DefaultNavigation, NavigationKibanaProvider, - NavigationTreeDefinition, + type NavigationTreeDefinition, getPresets, } from '@kbn/shared-ux-chrome-navigation'; import React from 'react'; import { i18n } from '@kbn/i18n'; -import { ServerlessPluginStart } from '@kbn/serverless/public'; +import type { ServerlessPluginStart } from '@kbn/serverless/public'; const navigationTree: NavigationTreeDefinition = { body: [ @@ -100,6 +100,10 @@ const navigationTree: NavigationTreeDefinition = { }, ], }, + { + type: 'navGroup', + ...getPresets('ml'), + }, ], }; diff --git a/x-pack/plugins/serverless_search/server/plugin.ts b/x-pack/plugins/serverless_search/server/plugin.ts index 760e59f82199c..767d51ff0c439 100644 --- a/x-pack/plugins/serverless_search/server/plugin.ts +++ b/x-pack/plugins/serverless_search/server/plugin.ts @@ -5,17 +5,25 @@ * 2.0. */ -import { IRouter, Logger, PluginInitializerContext, Plugin, CoreSetup } from '@kbn/core/server'; -import { SecurityPluginStart } from '@kbn/security-plugin/server'; +import type { + IRouter, + Logger, + PluginInitializerContext, + Plugin, + CoreSetup, +} from '@kbn/core/server'; +import type { SecurityPluginStart } from '@kbn/security-plugin/server'; import { registerApiKeyRoutes } from './routes/api_key_routes'; import { registerIndicesRoutes } from './routes/indices_routes'; -import { ServerlessSearchConfig } from './config'; -import { ServerlessSearchPluginSetup, ServerlessSearchPluginStart } from './types'; +import type { ServerlessSearchConfig } from './config'; +import type { + ServerlessSearchPluginSetup, + ServerlessSearchPluginStart, + SetupDependencies, + StartDependencies, +} from './types'; -interface StartDependencies { - security: SecurityPluginStart; -} export interface RouteDependencies { logger: Logger; router: IRouter; @@ -23,7 +31,13 @@ export interface RouteDependencies { } export class ServerlessSearchPlugin - implements Plugin + implements + Plugin< + ServerlessSearchPluginSetup, + ServerlessSearchPluginStart, + SetupDependencies, + StartDependencies + > { // @ts-ignore config is not used for now private readonly config: ServerlessSearchConfig; @@ -35,7 +49,10 @@ export class ServerlessSearchPlugin this.logger = initializerContext.logger.get(); } - public setup({ getStartServices, http }: CoreSetup) { + public setup( + { getStartServices, http }: CoreSetup, + pluginsSetup: SetupDependencies + ) { const router = http.createRouter(); getStartServices().then(([, { security }]) => { this.security = security; @@ -44,6 +61,8 @@ export class ServerlessSearchPlugin registerApiKeyRoutes(dependencies); registerIndicesRoutes(dependencies); }); + + pluginsSetup.ml.setFeaturesEnabled({ ad: false, dfa: false, nlp: true }); return {}; } diff --git a/x-pack/plugins/serverless_search/server/types.ts b/x-pack/plugins/serverless_search/server/types.ts index 6011e2eb60fa0..8e8f7f15a8124 100644 --- a/x-pack/plugins/serverless_search/server/types.ts +++ b/x-pack/plugins/serverless_search/server/types.ts @@ -5,7 +5,17 @@ * 2.0. */ +import type { SecurityPluginStart } from '@kbn/security-plugin/server'; +import type { MlPluginSetup } from '@kbn/ml-plugin/server'; + // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface ServerlessSearchPluginSetup {} // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface ServerlessSearchPluginStart {} + +export interface StartDependencies { + security: SecurityPluginStart; +} +export interface SetupDependencies { + ml: MlPluginSetup; +} diff --git a/x-pack/plugins/serverless_search/tsconfig.json b/x-pack/plugins/serverless_search/tsconfig.json index 1a52fe6187569..91a3f465ca4c6 100644 --- a/x-pack/plugins/serverless_search/tsconfig.json +++ b/x-pack/plugins/serverless_search/tsconfig.json @@ -27,6 +27,7 @@ "@kbn/security-plugin", "@kbn/cloud-plugin", "@kbn/share-plugin", + "@kbn/ml-plugin", "@kbn/management-cards-navigation", "@kbn/core-elasticsearch-server", ] diff --git a/x-pack/plugins/serverless_security/common/config.ts b/x-pack/plugins/serverless_security/common/config.ts index 05c1cb4f0b01b..63b173ff3930e 100644 --- a/x-pack/plugins/serverless_security/common/config.ts +++ b/x-pack/plugins/serverless_security/common/config.ts @@ -7,11 +7,18 @@ import { schema, TypeOf } from '@kbn/config-schema'; +export enum ProductLine { + security = 'security', + cloud = 'cloud', + endpoint = 'endpoint', +} + export const productLine = schema.oneOf([ - schema.literal('security'), - schema.literal('endpoint'), - schema.literal('cloud'), + schema.literal(ProductLine.security), + schema.literal(ProductLine.endpoint), + schema.literal(ProductLine.cloud), ]); + export type SecurityProductLine = TypeOf; export const productTier = schema.oneOf([schema.literal('essentials'), schema.literal('complete')]); diff --git a/x-pack/plugins/serverless_security/common/pli/pli_features.test.ts b/x-pack/plugins/serverless_security/common/pli/pli_features.test.ts index 0d46bcc421ec0..c00f8d7fdd9ec 100644 --- a/x-pack/plugins/serverless_security/common/pli/pli_features.test.ts +++ b/x-pack/plugins/serverless_security/common/pli/pli_features.test.ts @@ -6,6 +6,7 @@ */ import { getProductAppFeatures } from './pli_features'; import * as pliConfig from './pli_config'; +import { ProductLine } from '../config'; describe('getProductAppFeatures', () => { it('should return the essentials PLIs features', () => { @@ -18,7 +19,7 @@ describe('getProductAppFeatures', () => { }; const appFeatureKeys = getProductAppFeatures([ - { product_line: 'security', product_tier: 'essentials' }, + { product_line: ProductLine.security, product_tier: 'essentials' }, ]); expect(appFeatureKeys).toEqual(['foo']); @@ -34,7 +35,7 @@ describe('getProductAppFeatures', () => { }; const appFeatureKeys = getProductAppFeatures([ - { product_line: 'security', product_tier: 'complete' }, + { product_line: ProductLine.security, product_tier: 'complete' }, ]); expect(appFeatureKeys).toEqual(['foo', 'baz']); @@ -58,9 +59,9 @@ describe('getProductAppFeatures', () => { }; const appFeatureKeys = getProductAppFeatures([ - { product_line: 'security', product_tier: 'essentials' }, - { product_line: 'endpoint', product_tier: 'complete' }, - { product_line: 'cloud', product_tier: 'essentials' }, + { product_line: ProductLine.security, product_tier: 'essentials' }, + { product_line: ProductLine.endpoint, product_tier: 'complete' }, + { product_line: ProductLine.cloud, product_tier: 'essentials' }, ]); expect(appFeatureKeys).toEqual(['foo', 'bar', 'repeated', 'qux', 'quux', 'corge', 'garply']); diff --git a/x-pack/plugins/serverless_security/kibana.jsonc b/x-pack/plugins/serverless_security/kibana.jsonc index 4e9fcabdffd7d..daa7490a38651 100644 --- a/x-pack/plugins/serverless_security/kibana.jsonc +++ b/x-pack/plugins/serverless_security/kibana.jsonc @@ -13,10 +13,11 @@ "security" ], "requiredPlugins": [ - "serverless", + "kibanaReact", + "ml", "security", "securitySolution", - "kibanaReact" + "serverless" ], "optionalPlugins": [ "essSecurity" diff --git a/x-pack/plugins/serverless_security/public/components/get_started/get_started.test.tsx b/x-pack/plugins/serverless_security/public/components/get_started/get_started.test.tsx index a81d2525f6a84..ddf0b17985223 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/get_started.test.tsx +++ b/x-pack/plugins/serverless_security/public/components/get_started/get_started.test.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { render } from '@testing-library/react'; import { GetStartedComponent } from './get_started'; +import { SecurityProductTypes } from '../../../common/config'; jest.mock('./toggle_panel'); jest.mock('./welcome_panel'); @@ -20,9 +21,15 @@ jest.mock('@elastic/eui', () => { }; }); +const productTypes = [ + { product_line: 'security', product_tier: 'essentials' }, + { product_line: 'endpoint', product_tier: 'complete' }, + { product_line: 'cloud', product_tier: 'complete' }, +] as SecurityProductTypes; + describe('GetStartedComponent', () => { it('should render page title, subtitle, and description', () => { - const { getByText } = render(); + const { getByText } = render(); const pageTitle = getByText('Welcome'); const subtitle = getByText('Let’s get started'); @@ -35,8 +42,16 @@ describe('GetStartedComponent', () => { expect(description).toBeInTheDocument(); }); + it('should render Product Switch', () => { + const { getByTestId } = render(); + + const productSwitch = getByTestId('product-switch'); + + expect(productSwitch).toBeInTheDocument(); + }); + it('should render WelcomePanel and TogglePanel', () => { - const { getByTestId } = render(); + const { getByTestId } = render(); const welcomePanel = getByTestId('welcome-panel'); const togglePanel = getByTestId('toggle-panel'); diff --git a/x-pack/plugins/serverless_security/public/components/get_started/get_started.tsx b/x-pack/plugins/serverless_security/public/components/get_started/get_started.tsx index 1532528b6ff53..b29ba3b30d6a4 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/get_started.tsx +++ b/x-pack/plugins/serverless_security/public/components/get_started/get_started.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiTitle, useEuiTheme } from '@elastic/eui'; +import { EuiTitle, useEuiTheme, useEuiShadow } from '@elastic/eui'; import React from 'react'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import { css } from '@emotion/react'; @@ -17,10 +17,22 @@ import { GET_STARTED_PAGE_SUBTITLE, GET_STARTED_PAGE_TITLE, } from './translations'; +import { SecurityProductTypes } from '../../../common/config'; +import { ProductSwitch } from './product_switch'; +import { useTogglePanel } from './use_toggle_panel'; -export const GetStartedComponent: React.FC = () => { - const { euiTheme } = useEuiTheme(); +const CONTENT_WIDTH = 1150; +export const GetStartedComponent: React.FC<{ productTypes: SecurityProductTypes }> = ({ + productTypes, +}) => { + const { euiTheme } = useEuiTheme(); + const shadow = useEuiShadow('s'); + const { + onProductSwitchChanged, + onStepClicked, + state: { activeProducts, activeCards, finishedSteps }, + } = useTogglePanel({ productTypes }); return ( { `} > { > + + + - + ); diff --git a/x-pack/plugins/serverless_security/public/components/get_started/helpers.test.ts b/x-pack/plugins/serverless_security/public/components/get_started/helpers.test.ts index 0a4e32834bc94..f5511d4eaa85d 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/helpers.test.ts +++ b/x-pack/plugins/serverless_security/public/components/get_started/helpers.test.ts @@ -13,18 +13,18 @@ import { updateCard, } from './helpers'; import { - ActiveCard, + ActiveCards, Card, CardId, GetMoreFromElasticSecurityCardId, GetSetUpCardId, IntroductionSteps, - ProductId, Section, SectionId, StepId, } from './types'; import * as sectionsConfigs from './sections'; +import { ProductLine } from '../../../common/config'; const mockSections = jest.spyOn(sectionsConfigs, 'getSections'); describe('getCardTimeInMinutes', () => { it('should calculate the total time in minutes for a card correctly', () => { @@ -74,8 +74,8 @@ describe('getCardStepsLeft', () => { describe('isCardActive', () => { it('should return true if the card is active based on the active products', () => { - const card = { productTypeRequired: [ProductId.analytics, ProductId.cloud] } as Card; - const activeProducts = new Set([ProductId.analytics]); + const card = { productLineRequired: [ProductLine.security, ProductLine.cloud] } as Card; + const activeProducts = new Set([ProductLine.security]); const isActive = isCardActive(card, activeProducts); @@ -84,7 +84,7 @@ describe('isCardActive', () => { it('should return true if the card has no product type requirement', () => { const card = {} as Card; - const activeProducts = new Set([ProductId.analytics]); + const activeProducts = new Set([ProductLine.security]); const isActive = isCardActive(card, activeProducts); @@ -92,8 +92,8 @@ describe('isCardActive', () => { }); it('should return false if the card is not active based on the active products', () => { - const card = { productTypeRequired: [ProductId.analytics, ProductId.cloud] } as Card; - const activeProducts = new Set([ProductId.endpoint]); + const card = { productLineRequired: [ProductLine.security, ProductLine.cloud] } as Card; + const activeProducts = new Set([ProductLine.endpoint]); const isActive = isCardActive(card, activeProducts); @@ -140,7 +140,7 @@ describe('setupCards', () => { }; it('should set up active cards based on active products', () => { const finishedSteps = {} as unknown as Record>; - const activeProducts = new Set([ProductId.cloud]); + const activeProducts = new Set([ProductLine.cloud]); const activeCards = setupCards(finishedSteps, activeProducts); @@ -148,8 +148,8 @@ describe('setupCards', () => { ...analyticProductActiveCards, [SectionId.getSetUp]: { ...analyticProductActiveCards[SectionId.getSetUp], - [GetSetUpCardId.protectYourEnvironmentInRuntime]: { - id: GetSetUpCardId.protectYourEnvironmentInRuntime, + [GetSetUpCardId.protectYourEnvironmentInRealtime]: { + id: GetSetUpCardId.protectYourEnvironmentInRealtime, timeInMins: 0, stepsLeft: 0, }, @@ -159,7 +159,7 @@ describe('setupCards', () => { it('should skip inactive cards based on finished steps and active products', () => { const finishedSteps = {} as Record>; - const activeProducts = new Set([ProductId.analytics]); + const activeProducts = new Set([ProductLine.security]); const activeCards = setupCards(finishedSteps, activeProducts); @@ -171,7 +171,7 @@ describe('setupCards', () => { [GetSetUpCardId.introduction]: new Set([IntroductionSteps.watchOverviewVideo]), } as unknown as Record>; - const activeProducts: Set = new Set(); + const activeProducts: Set = new Set(); const activeCards = setupCards(finishedSteps, activeProducts); @@ -188,7 +188,7 @@ describe('setupCards', () => { const finishedSteps = { [GetSetUpCardId.introduction]: new Set([IntroductionSteps.watchOverviewVideo]), } as unknown as Record>; - const activeProducts = new Set([ProductId.analytics]); + const activeProducts = new Set([ProductLine.security]); const activeCards = setupCards(finishedSteps, activeProducts); @@ -202,7 +202,7 @@ describe('updateCard', () => { const finishedSteps = { [GetSetUpCardId.introduction]: new Set([IntroductionSteps.watchOverviewVideo]), } as unknown as Record>; - const activeProducts = new Set([ProductId.analytics, ProductId.cloud]); + const activeProducts = new Set([ProductLine.security, ProductLine.cloud]); const activeCards = { [SectionId.getSetUp]: { @@ -221,8 +221,8 @@ describe('updateCard', () => { timeInMins: 0, stepsLeft: 0, }, - [GetSetUpCardId.protectYourEnvironmentInRuntime]: { - id: GetSetUpCardId.protectYourEnvironmentInRuntime, + [GetSetUpCardId.protectYourEnvironmentInRealtime]: { + id: GetSetUpCardId.protectYourEnvironmentInRealtime, timeInMins: 0, stepsLeft: 0, }, @@ -244,7 +244,7 @@ describe('updateCard', () => { timeInMins: 0, }, }, - } as Record>; + } as ActiveCards; it('should update the active card based on finished steps and active products', () => { const sectionId = SectionId.getSetUp; @@ -273,7 +273,7 @@ describe('updateCard', () => { it('should return null if the card is inactive based on active products', () => { const sectionId = SectionId.getSetUp; - const cardId = GetSetUpCardId.protectYourEnvironmentInRuntime; + const cardId = GetSetUpCardId.protectYourEnvironmentInRealtime; const updatedCards = updateCard({ finishedSteps, diff --git a/x-pack/plugins/serverless_security/public/components/get_started/helpers.ts b/x-pack/plugins/serverless_security/public/components/get_started/helpers.ts index 1f4380e5afae6..0042550a34c4b 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/helpers.ts +++ b/x-pack/plugins/serverless_security/public/components/get_started/helpers.ts @@ -5,8 +5,9 @@ * 2.0. */ +import { ProductLine } from '../../../common/config'; import { getSections } from './sections'; -import { ActiveCard, Card, CardId, ProductId, SectionId, StepId } from './types'; +import { ActiveCard, ActiveCards, Card, CardId, SectionId, StepId } from './types'; export const getCardTimeInMinutes = (card: Card, stepsDone: Set) => card.steps?.reduce( @@ -18,13 +19,13 @@ export const getCardTimeInMinutes = (card: Card, stepsDone: Set) => export const getCardStepsLeft = (card: Card, stepsDone: Set) => (card.steps?.length ?? 0) - (stepsDone.size ?? 0); -export const isCardActive = (card: Card, activeProducts: Set) => - !card.productTypeRequired || - card.productTypeRequired?.some((condition) => activeProducts.has(condition)); +export const isCardActive = (card: Card, activeProducts: Set) => + !card.productLineRequired || + card.productLineRequired?.some((condition) => activeProducts.has(condition)); export const setupCards = ( finishedSteps: Record>, - activeProducts: Set + activeProducts: Set ) => activeProducts.size > 0 ? getSections().reduce((acc, section) => { @@ -46,7 +47,7 @@ export const setupCards = ( acc[section.id] = cardsInSections; } return acc; - }, {} as Record>) + }, {} as ActiveCards) : null; export const updateCard = ({ @@ -57,11 +58,11 @@ export const updateCard = ({ cardId, }: { finishedSteps: Record>; - activeProducts: Set; - activeCards: Record> | null; + activeProducts: Set; + activeCards: ActiveCards | null; sectionId: SectionId; cardId: CardId; -}): Record> | null => { +}): ActiveCards | null => { const sections = getSections(); const section = sections.find(({ id }) => id === sectionId); const cards = section?.cards; diff --git a/x-pack/plugins/serverless_security/public/components/get_started/index.tsx b/x-pack/plugins/serverless_security/public/components/get_started/index.tsx index 88227330bfde9..48326496d4422 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/index.tsx +++ b/x-pack/plugins/serverless_security/public/components/get_started/index.tsx @@ -13,14 +13,16 @@ import type { GetStartedComponent } from './types'; import { GetStarted } from './lazy'; import { KibanaServicesProvider } from '../../services'; import { ServerlessSecurityPluginStartDependencies } from '../../types'; +import { SecurityProductTypes } from '../../../common/config'; export const getSecurityGetStartedComponent = ( core: CoreStart, - pluginsStart: ServerlessSecurityPluginStartDependencies + pluginsStart: ServerlessSecurityPluginStartDependencies, + productTypes: SecurityProductTypes ): GetStartedComponent => { return () => ( - + ); }; diff --git a/x-pack/plugins/serverless_security/public/components/get_started/lazy.tsx b/x-pack/plugins/serverless_security/public/components/get_started/lazy.tsx index f968ad5d2ab22..3130bbe272f52 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/lazy.tsx +++ b/x-pack/plugins/serverless_security/public/components/get_started/lazy.tsx @@ -6,13 +6,14 @@ */ import React, { lazy, Suspense } from 'react'; import { EuiLoadingLogo } from '@elastic/eui'; +import { SecurityProductTypes } from '../../../common/config'; const GetStartedLazy = lazy(() => import('./get_started')); const centerLogoStyle = { display: 'flex', margin: 'auto' }; -export const GetStarted = () => ( +export const GetStarted = ({ productTypes }: { productTypes: SecurityProductTypes }) => ( }> - + ); diff --git a/x-pack/plugins/serverless_security/public/components/get_started/product_switch.test.tsx b/x-pack/plugins/serverless_security/public/components/get_started/product_switch.test.tsx index 49c0087f59830..29c01ddce5376 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/product_switch.test.tsx +++ b/x-pack/plugins/serverless_security/public/components/get_started/product_switch.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { render, fireEvent } from '@testing-library/react'; import { ProductSwitch } from './product_switch'; import { EuiThemeComputed } from '@elastic/eui'; -import { ProductId } from './types'; +import { ProductLine } from '../../../common/config'; describe('ProductSwitch', () => { const onProductSwitchChangedMock = jest.fn(); @@ -49,12 +49,12 @@ describe('ProductSwitch', () => { fireEvent.click(analyticsSwitch); expect(onProductSwitchChangedMock).toHaveBeenCalledWith( - expect.objectContaining({ id: 'analytics' }) + expect.objectContaining({ id: 'security' }) ); }); it('should have checked switches for activeProducts', () => { - const activeProducts = new Set([ProductId.analytics, ProductId.endpoint]); + const activeProducts = new Set([ProductLine.security, ProductLine.endpoint]); const { getByTestId } = render( { /> ); - const analyticsSwitch = getByTestId('analytics'); + const analyticsSwitch = getByTestId('security'); const cloudSwitch = getByTestId('cloud'); const endpointSwitch = getByTestId('endpoint'); diff --git a/x-pack/plugins/serverless_security/public/components/get_started/product_switch.tsx b/x-pack/plugins/serverless_security/public/components/get_started/product_switch.tsx index 910618c1a3f4c..822e5f3e69ca0 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/product_switch.tsx +++ b/x-pack/plugins/serverless_security/public/components/get_started/product_switch.tsx @@ -8,30 +8,30 @@ import { EuiPanel, EuiSwitch, EuiText, EuiThemeComputed, EuiTitle } from '@elastic/eui'; import { css } from '@emotion/react'; import React, { useMemo } from 'react'; +import { ProductLine } from '../../../common/config'; import * as i18n from './translations'; -import { ProductId, Switch } from './types'; +import { Switch } from './types'; const switches: Switch[] = [ { - id: ProductId.analytics, + id: ProductLine.security, label: i18n.ANALYTICS_SWITCH_LABEL, }, { - id: ProductId.cloud, + id: ProductLine.cloud, label: i18n.CLOUD_SWITCH_LABEL, }, { - id: ProductId.endpoint, + id: ProductLine.endpoint, label: i18n.ENDPOINT_SWITCH_LABEL, }, ]; const ProductSwitchComponent: React.FC<{ onProductSwitchChanged: (item: Switch) => void; - activeProducts: Set; - shadow?: string; + activeProducts: Set; euiTheme: EuiThemeComputed; -}> = ({ onProductSwitchChanged, activeProducts, euiTheme, shadow = '' }) => { +}> = ({ onProductSwitchChanged, activeProducts, euiTheme }) => { const switchNodes = useMemo( () => switches.map((item) => ( @@ -58,8 +58,7 @@ const ProductSwitchComponent: React.FC<{ paddingSize="none" hasShadow={false} css={css` - padding: ${euiTheme.base * 1.25}px ${euiTheme.base * 2.25}px; - ${shadow}; + padding: ${euiTheme.base * 1.25}px 0; `} borderRadius="none" > diff --git a/x-pack/plugins/serverless_security/public/components/get_started/reducer.test.ts b/x-pack/plugins/serverless_security/public/components/get_started/reducer.test.ts index 970b7ac6dbef0..259923080bfaa 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/reducer.test.ts +++ b/x-pack/plugins/serverless_security/public/components/get_started/reducer.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { ProductLine } from '../../../common/config'; import { reducer, getFinishedStepsInitialStates, @@ -12,12 +13,11 @@ import { getActiveCardsInitialStates, } from './reducer'; import { - ActiveCard, + ActiveCards, CardId, GetSetUpCardId, GetStartedPageActions, IntroductionSteps, - ProductId, SectionId, StepId, ToggleProductAction, @@ -28,25 +28,25 @@ import { describe('reducer', () => { it('should toggle section correctly', () => { const initialState = { - activeProducts: new Set([ProductId.analytics]), + activeProducts: new Set([ProductLine.security]), finishedSteps: {} as Record>, - activeCards: {} as Record> | null, + activeCards: {} as ActiveCards | null, }; const action: ToggleProductAction = { type: GetStartedPageActions.ToggleProduct, - payload: { section: ProductId.analytics }, + payload: { section: ProductLine.security }, }; const nextState = reducer(initialState, action); - expect(nextState.activeProducts.has(ProductId.analytics)).toBe(false); + expect(nextState.activeProducts.has(ProductLine.security)).toBe(false); expect(nextState.activeCards).toBeNull(); }); it('should add a finished step correctly', () => { const initialState = { - activeProducts: new Set([ProductId.analytics]), + activeProducts: new Set([ProductLine.security]), finishedSteps: {} as Record>, activeCards: { getSetUp: { @@ -56,7 +56,7 @@ describe('reducer', () => { timeInMins: 3, }, }, - } as unknown as Record> | null, + } as unknown as ActiveCards | null, }; const action: AddFinishedStepAction = { @@ -103,17 +103,17 @@ describe('getFinishedStepsInitialStates', () => { describe('getActiveSectionsInitialStates', () => { it('should return the initial states of active sections correctly', () => { - const activeProducts = [ProductId.analytics]; + const activeProducts = [ProductLine.security]; const initialStates = getActiveSectionsInitialStates({ activeProducts }); - expect(initialStates.has(ProductId.analytics)).toBe(true); + expect(initialStates.has(ProductLine.security)).toBe(true); }); }); describe('getActiveCardsInitialStates', () => { it('should return the initial states of active cards correctly', () => { - const activeProducts = new Set([ProductId.analytics]); + const activeProducts = new Set([ProductLine.security]); const finishedSteps = { [GetSetUpCardId.introduction]: new Set([IntroductionSteps.watchOverviewVideo]), } as unknown as Record>; diff --git a/x-pack/plugins/serverless_security/public/components/get_started/reducer.tsx b/x-pack/plugins/serverless_security/public/components/get_started/reducer.tsx index 2164bbee177bd..403bfb85e0a93 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/reducer.tsx +++ b/x-pack/plugins/serverless_security/public/components/get_started/reducer.tsx @@ -5,11 +5,11 @@ * 2.0. */ +import { ProductLine } from '../../../common/config'; import { setupCards, updateCard } from './helpers'; import { CardId, GetStartedPageActions, - ProductId, StepId, ToggleProductAction, TogglePanelReducer, @@ -80,13 +80,13 @@ export const getFinishedStepsInitialStates = ({ export const getActiveSectionsInitialStates = ({ activeProducts, }: { - activeProducts: ProductId[]; + activeProducts: ProductLine[]; }) => new Set(activeProducts); export const getActiveCardsInitialStates = ({ activeProducts, finishedSteps, }: { - activeProducts: Set; + activeProducts: Set; finishedSteps: Record>; }) => setupCards(finishedSteps, activeProducts); diff --git a/x-pack/plugins/serverless_security/public/components/get_started/sections.tsx b/x-pack/plugins/serverless_security/public/components/get_started/sections.tsx index 4a03eaff2dd76..33dbb6f62aa40 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/sections.tsx +++ b/x-pack/plugins/serverless_security/public/components/get_started/sections.tsx @@ -8,7 +8,6 @@ import React from 'react'; import { Section, - ProductId, SectionId, GetMoreFromElasticSecurityCardId, GetSetUpCardId, @@ -17,12 +16,7 @@ import { import * as i18n from './translations'; import respond from './images/respond.svg'; import protect from './images/protect.svg'; - -export const ActiveConditions = { - analyticsToggled: [ProductId.analytics], - cloudToggled: [ProductId.cloud], - endpointToggled: [ProductId.endpoint], -}; +import { ProductLine } from '../../../common/config'; export const introductionSteps = [ { @@ -85,11 +79,8 @@ export const sections: Section[] = [ { icon: { type: protect, size: 'xl' }, title: i18n.PROTECT_YOUR_ENVIRONMENT_TITLE, - id: GetSetUpCardId.protectYourEnvironmentInRuntime, - productTypeRequired: [ - ...ActiveConditions.cloudToggled, - ...ActiveConditions.endpointToggled, - ], + id: GetSetUpCardId.protectYourEnvironmentInRealtime, + productLineRequired: [ProductLine.cloud, ProductLine.endpoint], }, ], }, diff --git a/x-pack/plugins/serverless_security/public/components/get_started/toggle_panel.test.tsx b/x-pack/plugins/serverless_security/public/components/get_started/toggle_panel.test.tsx index a8350826ed6da..53f51cdc09a8b 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/toggle_panel.test.tsx +++ b/x-pack/plugins/serverless_security/public/components/get_started/toggle_panel.test.tsx @@ -5,10 +5,11 @@ * 2.0. */ import React from 'react'; -import { render, fireEvent } from '@testing-library/react'; +import { render } from '@testing-library/react'; import { TogglePanel } from './toggle_panel'; -import { getStartedStorage as mockGetStartedStorage } from '../../lib/get_started/storage'; import { useSetUpCardSections } from './use_setup_cards'; +import { ActiveCards, CardId, GetSetUpCardId, IntroductionSteps, SectionId, StepId } from './types'; +import { ProductLine } from '../../../common/config'; jest.mock('@elastic/eui', () => ({ ...jest.requireActual('@elastic/eui'), @@ -30,23 +31,57 @@ jest.mock('./use_setup_cards', () => ({ useSetUpCardSections: jest.fn(), })); +const finishedSteps = { + [GetSetUpCardId.introduction]: new Set([IntroductionSteps.watchOverviewVideo]), +} as unknown as Record>; +const activeProducts = new Set([ProductLine.security, ProductLine.cloud]); + +const activeCards = { + [SectionId.getSetUp]: { + [GetSetUpCardId.introduction]: { + id: GetSetUpCardId.introduction, + timeInMins: 3, + stepsLeft: 1, + }, + [GetSetUpCardId.bringInYourData]: { + id: GetSetUpCardId.bringInYourData, + timeInMins: 0, + stepsLeft: 0, + }, + [GetSetUpCardId.activateAndCreateRules]: { + id: GetSetUpCardId.activateAndCreateRules, + timeInMins: 0, + stepsLeft: 0, + }, + [GetSetUpCardId.protectYourEnvironmentInRealtime]: { + id: GetSetUpCardId.protectYourEnvironmentInRealtime, + timeInMins: 0, + stepsLeft: 0, + }, + }, +} as ActiveCards; + describe('TogglePanel', () => { - const mockUseSetUpCardSections = { setUpSections: jest.fn(() => null) }; + const mockUseSetUpCardSections = { + setUpSections: jest.fn(() =>
), + }; + const onStepClicked = jest.fn(); beforeEach(() => { jest.clearAllMocks(); (useSetUpCardSections as jest.Mock).mockReturnValue(mockUseSetUpCardSections); }); - it('should render the product switch ', () => { - const { getByTestId } = render(); - - expect(getByTestId('product-switch')).toBeInTheDocument(); - }); - it('should render empty prompt', () => { - const { getByText } = render(); + const { getByText } = render( + + ); expect(getByText(`Hmm, there doesn't seem to be anything there`)).toBeInTheDocument(); expect( @@ -54,16 +89,16 @@ describe('TogglePanel', () => { ).toBeInTheDocument(); }); - it('should toggle active sections when a product switch is changed', () => { - const { getByText } = render(); - - const analyticsSwitch = getByText('Analytics'); - const cloudSwitch = getByText('Cloud'); - - fireEvent.click(analyticsSwitch); - expect(mockGetStartedStorage.toggleActiveProductsInStorage).toHaveBeenCalledWith('analytics'); + it('should render sections', () => { + const { getByTestId } = render( + + ); - fireEvent.click(cloudSwitch); - expect(mockGetStartedStorage.toggleActiveProductsInStorage).toHaveBeenCalledWith('cloud'); + expect(getByTestId(`mock-sections`)).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/serverless_security/public/components/get_started/toggle_panel.tsx b/x-pack/plugins/serverless_security/public/components/get_started/toggle_panel.tsx index 3ce851fc8a232..af365b83bd80c 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/toggle_panel.tsx +++ b/x-pack/plugins/serverless_security/public/components/get_started/toggle_panel.tsx @@ -5,98 +5,46 @@ * 2.0. */ -import React, { useCallback, useMemo, useReducer } from 'react'; +import React from 'react'; import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, useEuiShadow, useEuiTheme } from '@elastic/eui'; import { css } from '@emotion/react'; -import { Switch, GetStartedPageActions, StepId, CardId, SectionId } from './types'; import * as i18n from './translations'; -import { ProductSwitch } from './product_switch'; import { useSetUpCardSections } from './use_setup_cards'; -import { getStartedStorage } from '../../lib/get_started/storage'; -import { - getActiveCardsInitialStates, - getActiveSectionsInitialStates, - getFinishedStepsInitialStates, - reducer, -} from './reducer'; -const TogglePanelComponent = () => { +import { ActiveCards, CardId, IntroductionSteps, SectionId } from './types'; +import { ProductLine } from '../../../common/config'; + +const TogglePanelComponent: React.FC<{ + finishedSteps: Record>; + activeCards: ActiveCards | null; + activeProducts: Set; + onStepClicked: ({ + stepId, + cardId, + sectionId, + }: { + stepId: IntroductionSteps; + cardId: CardId; + sectionId: SectionId; + }) => void; +}> = ({ finishedSteps, activeCards, activeProducts, onStepClicked }) => { const { euiTheme } = useEuiTheme(); const shadow = useEuiShadow('s'); - const { - getAllFinishedStepsFromStorage, - getActiveProductsFromStorage, - toggleActiveProductsInStorage, - addFinishedStepToStorage, - } = getStartedStorage; - const finishedStepsInitialStates = useMemo( - () => getFinishedStepsInitialStates({ finishedSteps: getAllFinishedStepsFromStorage() }), - [getAllFinishedStepsFromStorage] - ); - - const activeSectionsInitialStates = useMemo( - () => getActiveSectionsInitialStates({ activeProducts: getActiveProductsFromStorage() }), - [getActiveProductsFromStorage] - ); - - const activeCardsInitialStates = useMemo( - () => - getActiveCardsInitialStates({ - activeProducts: activeSectionsInitialStates, - finishedSteps: finishedStepsInitialStates, - }), - [activeSectionsInitialStates, finishedStepsInitialStates] - ); - const [state, dispatch] = useReducer(reducer, { - activeProducts: activeSectionsInitialStates, - finishedSteps: finishedStepsInitialStates, - activeCards: activeCardsInitialStates, - }); const { setUpSections } = useSetUpCardSections({ euiTheme, shadow }); - const onStepClicked = useCallback( - ({ stepId, cardId, sectionId }: { stepId: StepId; cardId: CardId; sectionId: SectionId }) => { - dispatch({ - type: GetStartedPageActions.AddFinishedStep, - payload: { stepId, cardId, sectionId }, - }); - addFinishedStepToStorage(cardId, stepId); - }, - [addFinishedStepToStorage] - ); const sectionNodes = setUpSections({ onStepClicked, - finishedSteps: state.finishedSteps, - activeCards: state.activeCards, + finishedSteps, + activeCards, }); - const onProductSwitchChanged = useCallback( - (section: Switch) => { - dispatch({ type: GetStartedPageActions.ToggleProduct, payload: { section: section.id } }); - toggleActiveProductsInStorage(section.id); - }, - [toggleActiveProductsInStorage] - ); return ( - - - - - {state.activeProducts.size > 0 ? ( + + {activeProducts.size > 0 ? ( sectionNodes ) : ( >; export enum SectionId { getSetUp = 'getSetUp', @@ -71,7 +68,7 @@ export enum GetSetUpCardId { activateAndCreateRules = 'activateAndCreateRules', bringInYourData = 'bringInYourData', introduction = 'introduction', - protectYourEnvironmentInRuntime = 'protectYourEnvironmentInRuntime', + protectYourEnvironmentInRealtime = 'protectYourEnvironmentInRealtime', } export enum IntroductionSteps { @@ -90,14 +87,14 @@ export interface ActiveCard { stepsLeft: number; } export interface TogglePanelReducer { - activeProducts: Set; + activeProducts: Set; finishedSteps: Record>; activeCards: Record> | null; } export interface ToggleProductAction { type: GetStartedPageActions.ToggleProduct; - payload: { section: ProductId }; + payload: { section: ProductLine }; } export interface AddFinishedStepAction { @@ -106,7 +103,7 @@ export interface AddFinishedStepAction { } export interface Switch { - id: ProductId; + id: ProductLine; label: string; } diff --git a/x-pack/plugins/serverless_security/public/components/get_started/use_setup_cards.test.tsx b/x-pack/plugins/serverless_security/public/components/get_started/use_setup_cards.test.tsx index dcf8c6a3cfb38..6e726fd9a002f 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/use_setup_cards.test.tsx +++ b/x-pack/plugins/serverless_security/public/components/get_started/use_setup_cards.test.tsx @@ -9,7 +9,7 @@ import { renderHook } from '@testing-library/react-hooks'; import { EuiThemeComputed } from '@elastic/eui'; import { useSetUpCardSections } from './use_setup_cards'; import { - ActiveCard, + ActiveCards, CardId, GetMoreFromElasticSecurityCardId, GetSetUpCardId, @@ -44,7 +44,7 @@ describe('useSetUpCardSections', () => { id: GetMoreFromElasticSecurityCardId.masterTheInvestigationsWorkflow, }, }, - } as Record>; + } as ActiveCards; const sections = result.current.setUpSections({ activeCards, diff --git a/x-pack/plugins/serverless_security/public/components/get_started/use_setup_cards.tsx b/x-pack/plugins/serverless_security/public/components/get_started/use_setup_cards.tsx index 082aadeae88ee..bd762042196ee 100644 --- a/x-pack/plugins/serverless_security/public/components/get_started/use_setup_cards.tsx +++ b/x-pack/plugins/serverless_security/public/components/get_started/use_setup_cards.tsx @@ -9,7 +9,7 @@ import { EuiSpacer, EuiThemeComputed } from '@elastic/eui'; import React, { useCallback } from 'react'; import { css } from '@emotion/react'; import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiTitle } from '@elastic/eui'; -import { ActiveCard, CardId, SectionId, StepId } from './types'; +import { ActiveCards, CardId, SectionId, StepId } from './types'; import { CardItem } from './card_item'; import { getSections } from './sections'; @@ -30,7 +30,7 @@ export const useSetUpCardSections = ({ }: { onStepClicked: (params: { stepId: StepId; cardId: CardId; sectionId: SectionId }) => void; finishedSteps: Record>; - activeCards: Record> | null; + activeCards: ActiveCards | null; sectionId: SectionId; }) => { const section = activeCards?.[sectionId]; @@ -63,7 +63,7 @@ export const useSetUpCardSections = ({ }: { onStepClicked: (params: { stepId: StepId; cardId: CardId; sectionId: SectionId }) => void; finishedSteps: Record>; - activeCards: Record> | null; + activeCards: ActiveCards | null; }) => getSections().reduce((acc, currentSection) => { const cardNodes = setUpCards({ diff --git a/x-pack/plugins/serverless_security/public/components/get_started/use_toggle_panel.test.tsx b/x-pack/plugins/serverless_security/public/components/get_started/use_toggle_panel.test.tsx new file mode 100644 index 0000000000000..6201009395935 --- /dev/null +++ b/x-pack/plugins/serverless_security/public/components/get_started/use_toggle_panel.test.tsx @@ -0,0 +1,239 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { renderHook, act } from '@testing-library/react-hooks'; +import { useTogglePanel } from './use_toggle_panel'; +import { getStartedStorage } from '../../lib/get_started/storage'; +import { ProductLine, SecurityProductTypes } from '../../../common/config'; +import { + GetMoreFromElasticSecurityCardId, + GetSetUpCardId, + IntroductionSteps, + SectionId, +} from './types'; + +jest.mock('../../lib/get_started/storage'); + +describe('useTogglePanel', () => { + const productTypes = [ + { product_line: 'security', product_tier: 'essentials' }, + { product_line: 'endpoint', product_tier: 'complete' }, + ] as SecurityProductTypes; + + beforeEach(() => { + jest.clearAllMocks(); + + (getStartedStorage.getAllFinishedStepsFromStorage as jest.Mock).mockReturnValue({ + [GetSetUpCardId.introduction]: new Set([IntroductionSteps.watchOverviewVideo]), + }); + (getStartedStorage.getActiveProductsFromStorage as jest.Mock).mockReturnValue([ + ProductLine.security, + ProductLine.cloud, + ProductLine.endpoint, + ]); + }); + + test('should initialize state with correct initial values - when no active products from local storage', () => { + (getStartedStorage.getAllFinishedStepsFromStorage as jest.Mock).mockReturnValue({}); + (getStartedStorage.getActiveProductsFromStorage as jest.Mock).mockReturnValue([]); + + const { result } = renderHook(() => useTogglePanel({ productTypes })); + + const { state } = result.current; + + expect(state.activeProducts).toEqual(new Set([ProductLine.security, ProductLine.endpoint])); + expect(state.finishedSteps).toEqual({}); + + expect(state.activeCards).toEqual( + expect.objectContaining({ + [SectionId.getSetUp]: { + [GetSetUpCardId.introduction]: { + id: GetSetUpCardId.introduction, + timeInMins: 3, + stepsLeft: 1, + }, + [GetSetUpCardId.activateAndCreateRules]: { + id: GetSetUpCardId.activateAndCreateRules, + timeInMins: 0, + stepsLeft: 0, + }, + [GetSetUpCardId.bringInYourData]: { + id: GetSetUpCardId.bringInYourData, + timeInMins: 0, + stepsLeft: 0, + }, + [GetSetUpCardId.protectYourEnvironmentInRealtime]: { + id: GetSetUpCardId.protectYourEnvironmentInRealtime, + timeInMins: 0, + stepsLeft: 0, + }, + }, + [SectionId.getMoreFromElasticSecurity]: { + [GetMoreFromElasticSecurityCardId.masterTheInvestigationsWorkflow]: { + id: GetMoreFromElasticSecurityCardId.masterTheInvestigationsWorkflow, + timeInMins: 0, + stepsLeft: 0, + }, + [GetMoreFromElasticSecurityCardId.optimizeYourWorkSpace]: { + id: GetMoreFromElasticSecurityCardId.optimizeYourWorkSpace, + timeInMins: 0, + stepsLeft: 0, + }, + [GetMoreFromElasticSecurityCardId.respondToThreats]: { + id: GetMoreFromElasticSecurityCardId.respondToThreats, + timeInMins: 0, + stepsLeft: 0, + }, + }, + }) + ); + }); + + test('should initialize state with correct initial values - when all products active', () => { + const { result } = renderHook(() => useTogglePanel({ productTypes })); + + const { state } = result.current; + + expect(state.activeProducts).toEqual( + new Set([ProductLine.security, ProductLine.cloud, ProductLine.endpoint]) + ); + expect(state.finishedSteps).toEqual({ + [GetSetUpCardId.introduction]: new Set([IntroductionSteps.watchOverviewVideo]), + }); + + expect(state.activeCards).toEqual( + expect.objectContaining({ + [SectionId.getSetUp]: { + [GetSetUpCardId.introduction]: { + id: GetSetUpCardId.introduction, + timeInMins: 0, + stepsLeft: 0, + }, + [GetSetUpCardId.activateAndCreateRules]: { + id: GetSetUpCardId.activateAndCreateRules, + timeInMins: 0, + stepsLeft: 0, + }, + [GetSetUpCardId.bringInYourData]: { + id: GetSetUpCardId.bringInYourData, + timeInMins: 0, + stepsLeft: 0, + }, + [GetSetUpCardId.protectYourEnvironmentInRealtime]: { + id: GetSetUpCardId.protectYourEnvironmentInRealtime, + timeInMins: 0, + stepsLeft: 0, + }, + }, + [SectionId.getMoreFromElasticSecurity]: { + [GetMoreFromElasticSecurityCardId.masterTheInvestigationsWorkflow]: { + id: GetMoreFromElasticSecurityCardId.masterTheInvestigationsWorkflow, + timeInMins: 0, + stepsLeft: 0, + }, + [GetMoreFromElasticSecurityCardId.optimizeYourWorkSpace]: { + id: GetMoreFromElasticSecurityCardId.optimizeYourWorkSpace, + timeInMins: 0, + stepsLeft: 0, + }, + [GetMoreFromElasticSecurityCardId.respondToThreats]: { + id: GetMoreFromElasticSecurityCardId.respondToThreats, + timeInMins: 0, + stepsLeft: 0, + }, + }, + }) + ); + }); + + test('should initialize state with correct initial values - when only security product active', () => { + (getStartedStorage.getActiveProductsFromStorage as jest.Mock).mockReturnValue([ + ProductLine.security, + ]); + const { result } = renderHook(() => useTogglePanel({ productTypes })); + + const { state } = result.current; + + expect(state.activeProducts).toEqual(new Set([ProductLine.security])); + expect(state.finishedSteps).toEqual({ + [GetSetUpCardId.introduction]: new Set([IntroductionSteps.watchOverviewVideo]), + }); + + expect(state.activeCards).toEqual( + expect.objectContaining({ + [SectionId.getSetUp]: { + [GetSetUpCardId.introduction]: { + id: GetSetUpCardId.introduction, + timeInMins: 0, + stepsLeft: 0, + }, + [GetSetUpCardId.activateAndCreateRules]: { + id: GetSetUpCardId.activateAndCreateRules, + timeInMins: 0, + stepsLeft: 0, + }, + [GetSetUpCardId.bringInYourData]: { + id: GetSetUpCardId.bringInYourData, + timeInMins: 0, + stepsLeft: 0, + }, + }, + [SectionId.getMoreFromElasticSecurity]: { + [GetMoreFromElasticSecurityCardId.masterTheInvestigationsWorkflow]: { + id: GetMoreFromElasticSecurityCardId.masterTheInvestigationsWorkflow, + timeInMins: 0, + stepsLeft: 0, + }, + [GetMoreFromElasticSecurityCardId.optimizeYourWorkSpace]: { + id: GetMoreFromElasticSecurityCardId.optimizeYourWorkSpace, + timeInMins: 0, + stepsLeft: 0, + }, + [GetMoreFromElasticSecurityCardId.respondToThreats]: { + id: GetMoreFromElasticSecurityCardId.respondToThreats, + timeInMins: 0, + stepsLeft: 0, + }, + }, + }) + ); + }); + + test('should call addFinishedStepToStorage', () => { + const { result } = renderHook(() => useTogglePanel({ productTypes })); + + const { onStepClicked } = result.current; + + act(() => { + onStepClicked({ + stepId: IntroductionSteps.watchOverviewVideo, + cardId: GetSetUpCardId.introduction, + sectionId: SectionId.getSetUp, + }); + }); + + expect(getStartedStorage.addFinishedStepToStorage).toHaveBeenCalledTimes(1); + expect(getStartedStorage.addFinishedStepToStorage).toHaveBeenCalledWith( + GetSetUpCardId.introduction, + IntroductionSteps.watchOverviewVideo + ); + }); + + test('should call toggleActiveProductsInStorage', () => { + const { result } = renderHook(() => useTogglePanel({ productTypes })); + + const { onProductSwitchChanged } = result.current; + + act(() => { + onProductSwitchChanged({ id: ProductLine.security, label: 'Analytics' }); + }); + + expect(getStartedStorage.toggleActiveProductsInStorage).toHaveBeenCalledTimes(1); + expect(getStartedStorage.toggleActiveProductsInStorage).toHaveBeenCalledWith( + ProductLine.security + ); + }); +}); diff --git a/x-pack/plugins/serverless_security/public/components/get_started/use_toggle_panel.tsx b/x-pack/plugins/serverless_security/public/components/get_started/use_toggle_panel.tsx new file mode 100644 index 0000000000000..f449478572c45 --- /dev/null +++ b/x-pack/plugins/serverless_security/public/components/get_started/use_toggle_panel.tsx @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback, useMemo, useReducer } from 'react'; +import { ProductLine, SecurityProductTypes } from '../../../common/config'; +import { getStartedStorage } from '../../lib/get_started/storage'; +import { + getActiveCardsInitialStates, + getActiveSectionsInitialStates, + getFinishedStepsInitialStates, + reducer, +} from './reducer'; +import { CardId, GetStartedPageActions, SectionId, StepId, Switch } from './types'; + +export const useTogglePanel = ({ productTypes }: { productTypes: SecurityProductTypes }) => { + const { + getAllFinishedStepsFromStorage, + getActiveProductsFromStorage, + toggleActiveProductsInStorage, + addFinishedStepToStorage, + } = getStartedStorage; + + const finishedStepsInitialStates = useMemo( + () => getFinishedStepsInitialStates({ finishedSteps: getAllFinishedStepsFromStorage() }), + [getAllFinishedStepsFromStorage] + ); + + const activeSectionsInitialStates = useMemo(() => { + const activeProductsFromStorage = getActiveSectionsInitialStates({ + activeProducts: getActiveProductsFromStorage(), + }); + return activeProductsFromStorage.size > 0 + ? activeProductsFromStorage + : new Set(productTypes.map(({ product_line: productLine }) => ProductLine[productLine])) ?? + new Set([ProductLine.security, ProductLine.endpoint, ProductLine.cloud]); + }, [getActiveProductsFromStorage, productTypes]); + + const activeCardsInitialStates = useMemo( + () => + getActiveCardsInitialStates({ + activeProducts: activeSectionsInitialStates, + finishedSteps: finishedStepsInitialStates, + }), + [activeSectionsInitialStates, finishedStepsInitialStates] + ); + + const [state, dispatch] = useReducer(reducer, { + activeProducts: activeSectionsInitialStates, + finishedSteps: finishedStepsInitialStates, + activeCards: activeCardsInitialStates, + }); + + const onStepClicked = useCallback( + ({ stepId, cardId, sectionId }: { stepId: StepId; cardId: CardId; sectionId: SectionId }) => { + dispatch({ + type: GetStartedPageActions.AddFinishedStep, + payload: { stepId, cardId, sectionId }, + }); + addFinishedStepToStorage(cardId, stepId); + }, + [addFinishedStepToStorage] + ); + + const onProductSwitchChanged = useCallback( + (section: Switch) => { + dispatch({ type: GetStartedPageActions.ToggleProduct, payload: { section: section.id } }); + toggleActiveProductsInStorage(section.id); + }, + [toggleActiveProductsInStorage] + ); + + return { state, onStepClicked, onProductSwitchChanged }; +}; diff --git a/x-pack/plugins/serverless_security/public/components/upselling/register_upsellings.test.tsx b/x-pack/plugins/serverless_security/public/components/upselling/register_upsellings.test.tsx index b2a1390ee098f..304ee02de256c 100644 --- a/x-pack/plugins/serverless_security/public/components/upselling/register_upsellings.test.tsx +++ b/x-pack/plugins/serverless_security/public/components/upselling/register_upsellings.test.tsx @@ -8,7 +8,7 @@ import { UpsellingService } from '@kbn/security-solution-plugin/public'; import { ALL_APP_FEATURE_KEYS } from '@kbn/security-solution-plugin/common'; import { registerUpsellings, upsellingPages, upsellingSections } from './register_upsellings'; -import type { SecurityProductTypes } from '../../../common/config'; +import { ProductLine, SecurityProductTypes } from '../../../common/config'; const mockGetProductAppFeatures = jest.fn(); jest.mock('../../../common/pli/pli_features', () => ({ @@ -16,9 +16,9 @@ jest.mock('../../../common/pli/pli_features', () => ({ })); const allProductTypes: SecurityProductTypes = [ - { product_line: 'security', product_tier: 'complete' }, - { product_line: 'endpoint', product_tier: 'complete' }, - { product_line: 'cloud', product_tier: 'complete' }, + { product_line: ProductLine.security, product_tier: 'complete' }, + { product_line: ProductLine.endpoint, product_tier: 'complete' }, + { product_line: ProductLine.cloud, product_tier: 'complete' }, ]; describe('registerUpsellings', () => { diff --git a/x-pack/plugins/serverless_security/public/lib/get_started/storage.test.ts b/x-pack/plugins/serverless_security/public/lib/get_started/storage.test.ts index 158c271470768..e55c4ef28948b 100644 --- a/x-pack/plugins/serverless_security/public/lib/get_started/storage.test.ts +++ b/x-pack/plugins/serverless_security/public/lib/get_started/storage.test.ts @@ -6,14 +6,10 @@ */ import { getStartedStorage } from './storage'; -import { - GetSetUpCardId, - IntroductionSteps, - ProductId, - StepId, -} from '../../components/get_started/types'; +import { GetSetUpCardId, IntroductionSteps, StepId } from '../../components/get_started/types'; import { storage } from '../storage'; import { MockStorage } from '../__mocks__/storage'; +import { ProductLine } from '../../../common/config'; jest.mock('../storage'); @@ -33,13 +29,13 @@ describe('useStorage', () => { }); it('should toggle active products in storage', () => { - expect(getStartedStorage.toggleActiveProductsInStorage(ProductId.analytics)).toEqual([ - ProductId.analytics, + expect(getStartedStorage.toggleActiveProductsInStorage(ProductLine.security)).toEqual([ + ProductLine.security, ]); - expect(mockStorage.set).toHaveBeenCalledWith('ACTIVE_PRODUCTS', [ProductId.analytics]); + expect(mockStorage.set).toHaveBeenCalledWith('ACTIVE_PRODUCTS', [ProductLine.security]); - mockStorage.set('ACTIVE_PRODUCTS', [ProductId.analytics]); - expect(getStartedStorage.toggleActiveProductsInStorage(ProductId.analytics)).toEqual([]); + mockStorage.set('ACTIVE_PRODUCTS', [ProductLine.security]); + expect(getStartedStorage.toggleActiveProductsInStorage(ProductLine.security)).toEqual([]); expect(mockStorage.set).toHaveBeenCalledWith('ACTIVE_PRODUCTS', []); }); diff --git a/x-pack/plugins/serverless_security/public/lib/get_started/storage.ts b/x-pack/plugins/serverless_security/public/lib/get_started/storage.ts index 691cbb5f102f0..1d9c52813c832 100644 --- a/x-pack/plugins/serverless_security/public/lib/get_started/storage.ts +++ b/x-pack/plugins/serverless_security/public/lib/get_started/storage.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { CardId, ProductId, StepId } from '../../components/get_started/types'; +import { ProductLine } from '../../../common/config'; +import { CardId, StepId } from '../../components/get_started/types'; import { storage } from '../storage'; export const ACTIVE_PRODUCTS_STORAGE_KEY = 'ACTIVE_PRODUCTS'; @@ -13,12 +14,12 @@ export const FINISHED_STEPS_STORAGE_KEY = 'FINISHED_STEPS'; export const getStartedStorage = { getActiveProductsFromStorage: () => { - const activeProducts: ProductId[] = storage.get(ACTIVE_PRODUCTS_STORAGE_KEY); + const activeProducts: ProductLine[] = storage.get(ACTIVE_PRODUCTS_STORAGE_KEY); return activeProducts ?? new Array(); }, - toggleActiveProductsInStorage: (productId: ProductId) => { - const activeProducts: ProductId[] = - storage.get(ACTIVE_PRODUCTS_STORAGE_KEY) ?? new Array(); + toggleActiveProductsInStorage: (productId: ProductLine) => { + const activeProducts: ProductLine[] = + storage.get(ACTIVE_PRODUCTS_STORAGE_KEY) ?? new Array(); const index = activeProducts.indexOf(productId); if (index < 0) { activeProducts.push(productId); diff --git a/x-pack/plugins/serverless_security/public/plugin.ts b/x-pack/plugins/serverless_security/public/plugin.ts index f0729330de4af..b93be1b16dcd4 100644 --- a/x-pack/plugins/serverless_security/public/plugin.ts +++ b/x-pack/plugins/serverless_security/public/plugin.ts @@ -48,7 +48,9 @@ export class ServerlessSecurityPlugin const { securitySolution, serverless } = startDeps; securitySolution.setIsSidebarEnabled(false); - securitySolution.setGetStartedPage(getSecurityGetStartedComponent(core, startDeps)); + securitySolution.setGetStartedPage( + getSecurityGetStartedComponent(core, startDeps, this.config.productTypes) + ); serverless.setProjectHome('/app/security'); serverless.setSideNavComponent(getSecuritySideNavComponent(core, startDeps)); diff --git a/x-pack/plugins/serverless_security/server/plugin.ts b/x-pack/plugins/serverless_security/server/plugin.ts index 6cf98846bcd23..aae5106d9a0d2 100644 --- a/x-pack/plugins/serverless_security/server/plugin.ts +++ b/x-pack/plugins/serverless_security/server/plugin.ts @@ -5,11 +5,11 @@ * 2.0. */ -import { PluginInitializerContext, Plugin, CoreSetup } from '@kbn/core/server'; -import { ServerlessSecurityConfig } from './config'; +import type { PluginInitializerContext, Plugin, CoreSetup } from '@kbn/core/server'; +import type { ServerlessSecurityConfig } from './config'; import { getProductAppFeatures } from '../common/pli/pli_features'; -import { +import type { ServerlessSecurityPluginSetup, ServerlessSecurityPluginStart, ServerlessSecurityPluginSetupDependencies, @@ -41,6 +41,8 @@ export class ServerlessSecurityPlugin pluginsSetup.securitySolution.setAppFeatures(getProductAppFeatures(this.config.productTypes)); } + pluginsSetup.ml.setFeaturesEnabled({ ad: true, dfa: true, nlp: false }); + return {}; } diff --git a/x-pack/plugins/serverless_security/server/types.ts b/x-pack/plugins/serverless_security/server/types.ts index 6756351c9bf6e..9cd615265338d 100644 --- a/x-pack/plugins/serverless_security/server/types.ts +++ b/x-pack/plugins/serverless_security/server/types.ts @@ -7,12 +7,13 @@ import type { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/server'; import type { PluginSetupContract, PluginStartContract } from '@kbn/features-plugin/server'; -import { +import type { PluginSetup as SecuritySolutionPluginSetup, PluginStart as SecuritySolutionPluginStart, } from '@kbn/security-solution-plugin/server'; import type { EssSecurityPluginSetup } from '@kbn/ess-security/server'; +import type { MlPluginSetup } from '@kbn/ml-plugin/server'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface ServerlessSecurityPluginSetup {} @@ -24,6 +25,7 @@ export interface ServerlessSecurityPluginSetupDependencies { securitySolution: SecuritySolutionPluginSetup; features: PluginSetupContract; essSecurity: EssSecurityPluginSetup; + ml: MlPluginSetup; } export interface ServerlessSecurityPluginStartDependencies { diff --git a/x-pack/plugins/serverless_security/tsconfig.json b/x-pack/plugins/serverless_security/tsconfig.json index 9fa63eb6ed210..69d976d324105 100644 --- a/x-pack/plugins/serverless_security/tsconfig.json +++ b/x-pack/plugins/serverless_security/tsconfig.json @@ -29,6 +29,7 @@ "@kbn/shared-ux-page-kibana-template", "@kbn/features-plugin", "@kbn/ess-security", - "@kbn/kibana-utils-plugin", + "@kbn/ml-plugin", + "@kbn/kibana-utils-plugin" ] } diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/manual_test_runs/actions.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/manual_test_runs/actions.ts index b1eb68738bc50..65cf3ab97d070 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/manual_test_runs/actions.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/manual_test_runs/actions.ts @@ -13,8 +13,12 @@ import { createAsyncAction } from '../utils/actions'; export const toggleTestNowFlyoutAction = createAction('TOGGLE TEST NOW FLYOUT ACTION'); export const hideTestNowFlyoutAction = createAction('HIDE ALL TEST NOW FLYOUT ACTION'); +export interface TestNowPayload { + configId: string; + name: string; +} export const manualTestMonitorAction = createAsyncAction< - { configId: string; name: string }, + TestNowPayload, TestNowResponse | undefined >('TEST_NOW_MONITOR_ACTION'); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/manual_test_runs/index.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/manual_test_runs/index.ts index 73f6ec9f76a22..53a99a651ee93 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/manual_test_runs/index.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/manual_test_runs/index.ts @@ -10,12 +10,14 @@ import { createReducer, PayloadAction } from '@reduxjs/toolkit'; import { WritableDraft } from 'immer/dist/types/types-external'; import { IHttpFetchError } from '@kbn/core-http-browser'; +import { ActionPayload } from '../utils/actions'; import { TestNowResponse } from '../../../../../common/types'; import { clearTestNowMonitorAction, hideTestNowFlyoutAction, manualTestMonitorAction, manualTestRunUpdateAction, + TestNowPayload, toggleTestNowFlyoutAction, } from './actions'; import { @@ -57,10 +59,7 @@ export const manualTestRunsReducer = createReducer(initialState, (builder) => { builder .addCase( String(manualTestMonitorAction.get), - ( - state: WritableDraft, - action: PayloadAction<{ configId: string; name: string }> - ) => { + (state: WritableDraft, action: PayloadAction) => { state = Object.values(state).reduce((acc, curr) => { acc[curr.configId] = { ...curr, @@ -98,9 +97,12 @@ export const manualTestRunsReducer = createReducer(initialState, (builder) => { ) .addCase( String(manualTestMonitorAction.fail), - (state: WritableDraft, action: PayloadAction) => { + ( + state: WritableDraft, + action: ActionPayload + ) => { const fetchError = action.payload as unknown as IHttpFetchError; - if (fetchError?.request.url) { + if (fetchError?.request?.url) { const { name, message } = fetchError; const [, errorMonitor] = @@ -117,10 +119,10 @@ export const manualTestRunsReducer = createReducer(initialState, (builder) => { }; } } - - if (action.payload.configId) { - state[action.payload.configId] = { - ...state[action.payload.configId], + const configId = action.payload.configId ?? action.payload.getPayload?.configId; + if (configId) { + state[configId] = { + ...state[configId], status: TestRunStatus.COMPLETED, errors: action.payload.errors, fetchError: undefined, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/utils/actions.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/utils/actions.ts index 2a8d13a0f6025..789728faf70d9 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/utils/actions.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/utils/actions.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { createAction } from '@reduxjs/toolkit'; +import { createAction, PayloadAction } from '@reduxjs/toolkit'; import type { IHttpSerializedFetchError } from './http_error'; export function createAsyncAction< @@ -31,3 +31,7 @@ function prepareForTimestamp(payload: Payload) { }, }; } + +export interface ActionPayload extends PayloadAction

{ + payload: P & { getPayload?: G }; +} diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor.ts index 8ff63923aba16..f675f10d6be92 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor.ts @@ -316,9 +316,13 @@ const setupGettingStarted = (configId: string, routeContext: RouteContext) => { if (gettingStarted) { // ignore await, since we don't want to block the response - triggerTestNow(configId, routeContext).then(() => { - server.logger.debug(`Successfully triggered test for monitor: ${configId}`); - }); + triggerTestNow(configId, routeContext) + .then(() => { + server.logger.debug(`Successfully triggered test for monitor: ${configId}`); + }) + .catch((e) => { + server.logger.error(`Error triggering test for monitor: ${configId}: ${e}`); + }); } } catch (e) { server.logger.info(`Error triggering test for getting started monitor: ${configId}`); diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/delete_monitor.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/delete_monitor.ts index 032a48695ec97..91a7a03429030 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/delete_monitor.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/delete_monitor.ts @@ -6,7 +6,6 @@ */ import { schema } from '@kbn/config-schema'; import { SavedObjectsClientContract, SavedObjectsErrorHelpers } from '@kbn/core/server'; -import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; import { SyntheticsServerSetup } from '../../types'; import { RouteContext, SyntheticsRestApiRouteFactory } from '../types'; import { syntheticsMonitorType } from '../../../common/types/saved_objects'; @@ -69,19 +68,19 @@ export const deleteMonitor = async ({ routeContext: RouteContext; monitorId: string; }) => { - const { savedObjectsClient, server, syntheticsMonitorClient, request } = routeContext; + const { spaceId, savedObjectsClient, server, syntheticsMonitorClient, request } = routeContext; const { logger, telemetry, stackVersion } = server; const { monitor, monitorWithSecret } = await getMonitorToDelete( monitorId, savedObjectsClient, - server + server, + spaceId ); let deletePromise; try { - const spaceId = server.spaces?.spacesService.getSpaceId(request) ?? DEFAULT_SPACE_ID; deletePromise = savedObjectsClient.delete(syntheticsMonitorType, monitorId); const deleteSyncPromise = syntheticsMonitorClient.deleteMonitors( @@ -140,7 +139,8 @@ export const deleteMonitor = async ({ const getMonitorToDelete = async ( monitorId: string, soClient: SavedObjectsClientContract, - server: SyntheticsServerSetup + server: SyntheticsServerSetup, + spaceId: string ) => { const encryptedSOClient = server.encryptedSavedObjects.getClient(); @@ -148,7 +148,10 @@ const getMonitorToDelete = async ( const monitor = await encryptedSOClient.getDecryptedAsInternalUser( syntheticsMonitorType, - monitorId + monitorId, + { + namespace: spaceId, + } ); return { monitor: normalizeSecrets(monitor), monitorWithSecret: normalizeSecrets(monitor) }; } catch (e) { diff --git a/x-pack/plugins/synthetics/server/routes/synthetics_service/test_now_monitor.ts b/x-pack/plugins/synthetics/server/routes/synthetics_service/test_now_monitor.ts index 22fc903055eba..bf34e20039867 100644 --- a/x-pack/plugins/synthetics/server/routes/synthetics_service/test_now_monitor.ts +++ b/x-pack/plugins/synthetics/server/routes/synthetics_service/test_now_monitor.ts @@ -40,7 +40,10 @@ export const triggerTestNow = async ( const monitorWithSecrets = await encryptedClient.getDecryptedAsInternalUser( syntheticsMonitorType, - monitorId + monitorId, + { + namespace: spaceId, + } ); const normalizedMonitor = normalizeSecrets(monitorWithSecrets); diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 654e2259983cd..121e0dce922c9 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -1114,9 +1114,6 @@ "dashboard.panelStorageError.setError": "Une erreur s'est produite lors de la définition des modifications non enregistrées : {message}", "dashboard.share.defaultDashboardTitle": "Tableau de bord [{date}]", "dashboard.strings.dashboardEditTitle": "Modification de {title}", - "dashboard.topNav.cloneModal.dashboardExistsDescription": "Cliquez sur {confirmClone} pour cloner le tableau de bord avec le titre dupliqué.", - "dashboard.topNav.cloneModal.dashboardExistsTitle": "Il existe déjà un tableau de bord avec le titre {newDashboardName}.", - "dashboard.topNav.showCloneModal.dashboardCopyTitle": "Copie de {title}", "dashboard.actions.DownloadCreateDrilldownAction.displayName": "Télécharger au format CSV", "dashboard.actions.downloadOptionsUnsavedFilename": "sans titre", "dashboard.actions.toggleExpandPanelMenuItem.expandedDisplayName": "Minimiser", @@ -1128,7 +1125,6 @@ "dashboard.appLeaveConfirmModal.unsavedChangesTitle": "Modifications non enregistrées", "dashboard.badge.readOnly.text": "Lecture seule", "dashboard.badge.readOnly.tooltip": "Impossible d'enregistrer les tableaux de bord", - "dashboard.cloneModal.cloneDashboardTitleAriaLabel": "Titre du tableau de bord cloné", "dashboard.createConfirmModal.cancelButtonLabel": "Annuler", "dashboard.createConfirmModal.confirmButtonLabel": "Redémarrer", "dashboard.createConfirmModal.continueButtonLabel": "Poursuivre les modifications", @@ -1221,11 +1217,6 @@ "dashboard.solutionToolbar.addPanelButtonLabel": "Créer une visualisation", "dashboard.solutionToolbar.editorMenuButtonLabel": "Sélectionner un type", "dashboard.solutionToolbar.quickCreateButtonGroupLegend": "Raccourcis vers les types de visualisation populaires", - "dashboard.topNav.cloneModal.cancelButtonLabel": "Annuler", - "dashboard.topNav.cloneModal.cloneDashboardModalHeaderTitle": "Cloner le tableau de bord", - "dashboard.topNav.cloneModal.confirmButtonLabel": "Confirmer le clonage", - "dashboard.topNav.cloneModal.confirmCloneDescription": "Confirmer le clonage", - "dashboard.topNav.cloneModal.enterNewNameForDashboardDescription": "Veuillez saisir un autre nom pour votre tableau de bord.", "dashboard.topNav.labsButtonAriaLabel": "ateliers", "dashboard.topNav.labsConfigDescription": "Ateliers", "dashboard.topNav.saveModal.objectType": "tableau de bord", @@ -5682,7 +5673,6 @@ "unifiedHistogram.lensTitle": "Modifier la visualisation", "unifiedHistogram.resetChartHeight": "Réinitialiser à la hauteur par défaut", "unifiedHistogram.showChart": "Afficher le graphique", - "unifiedHistogram.suggestionSelectorLabel": "Visualisation", "unifiedHistogram.suggestionSelectorPlaceholder": "Sélectionner la visualisation", "unifiedHistogram.timeIntervals": "Intervalles de temps", "unifiedHistogram.timeIntervalWithValueWarning": "Avertissement", @@ -39329,7 +39319,6 @@ "xpack.cloudDataMigration.upgrade.text": "Effectuer la mise à niveau vers les versions plus récentes beaucoup plus facilement", "xpack.cloudLinks.deploymentLinkLabel": "Gérer ce déploiement", "xpack.cloudLinks.setupGuide": "Guides de configuration", - "xpack.cloudLinks.userMenuLinks.accountLinkText": "Compte et facturation", "xpack.cloudLinks.userMenuLinks.profileLinkText": "Modifier le profil", "xpack.dashboard.components.DashboardDrilldownConfig.chooseDestinationDashboard": "Choisir le tableau de bord de destination", "xpack.dashboard.components.DashboardDrilldownConfig.openInNewTab": "Ouvrir le tableau de bord dans un nouvel onglet", @@ -39433,4 +39422,4 @@ "xpack.painlessLab.title": "Painless Lab", "xpack.painlessLab.walkthroughButtonLabel": "Présentation" } -} \ No newline at end of file +} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 70ae6e7263d36..2789358b1e0f2 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -1114,9 +1114,6 @@ "dashboard.panelStorageError.setError": "保存されていない変更の設定中にエラーが発生しました:{message}", "dashboard.share.defaultDashboardTitle": "ダッシュボード[{date}]", "dashboard.strings.dashboardEditTitle": "{title}の編集中", - "dashboard.topNav.cloneModal.dashboardExistsDescription": "{confirmClone}をクリックして重複タイトルでダッシュボードのクローンを作成します。", - "dashboard.topNav.cloneModal.dashboardExistsTitle": "「{newDashboardName}」というタイトルのダッシュボードがすでに存在します。", - "dashboard.topNav.showCloneModal.dashboardCopyTitle": "{title}コピー", "dashboard.actions.DownloadCreateDrilldownAction.displayName": "CSV をダウンロード", "dashboard.actions.downloadOptionsUnsavedFilename": "無題", "dashboard.actions.toggleExpandPanelMenuItem.expandedDisplayName": "最小化", @@ -1128,7 +1125,6 @@ "dashboard.appLeaveConfirmModal.unsavedChangesTitle": "保存されていない変更", "dashboard.badge.readOnly.text": "読み取り専用", "dashboard.badge.readOnly.tooltip": "ダッシュボードを保存できません", - "dashboard.cloneModal.cloneDashboardTitleAriaLabel": "クローンダッシュボードタイトル", "dashboard.createConfirmModal.cancelButtonLabel": "キャンセル", "dashboard.createConfirmModal.confirmButtonLabel": "やり直す", "dashboard.createConfirmModal.continueButtonLabel": "編集を続行", @@ -1221,11 +1217,6 @@ "dashboard.solutionToolbar.addPanelButtonLabel": "ビジュアライゼーションを作成", "dashboard.solutionToolbar.editorMenuButtonLabel": "タイプを選択してください", "dashboard.solutionToolbar.quickCreateButtonGroupLegend": "よく使用されるビジュアライゼーションタイプへのショートカット", - "dashboard.topNav.cloneModal.cancelButtonLabel": "キャンセル", - "dashboard.topNav.cloneModal.cloneDashboardModalHeaderTitle": "ダッシュボードのクローンを作成", - "dashboard.topNav.cloneModal.confirmButtonLabel": "クローンの確認", - "dashboard.topNav.cloneModal.confirmCloneDescription": "クローンの確認", - "dashboard.topNav.cloneModal.enterNewNameForDashboardDescription": "ダッシュボードの新しい名前を入力してください。", "dashboard.topNav.labsButtonAriaLabel": "ラボ", "dashboard.topNav.labsConfigDescription": "ラボ", "dashboard.topNav.saveModal.objectType": "ダッシュボード", @@ -5683,7 +5674,6 @@ "unifiedHistogram.lensTitle": "ビジュアライゼーションを編集", "unifiedHistogram.resetChartHeight": "デフォルトの高さにリセット", "unifiedHistogram.showChart": "グラフを表示", - "unifiedHistogram.suggestionSelectorLabel": "ビジュアライゼーション", "unifiedHistogram.suggestionSelectorPlaceholder": "ビジュアライゼーションを選択", "unifiedHistogram.timeIntervals": "時間間隔", "unifiedHistogram.timeIntervalWithValueWarning": "警告", @@ -39303,7 +39293,6 @@ "xpack.cloudDataMigration.upgrade.text": "新しいバージョンへのアップグレードがより簡単に", "xpack.cloudLinks.deploymentLinkLabel": "このデプロイの管理", "xpack.cloudLinks.setupGuide": "セットアップガイド", - "xpack.cloudLinks.userMenuLinks.accountLinkText": "会計・請求", "xpack.cloudLinks.userMenuLinks.profileLinkText": "プロフィールを編集", "xpack.dashboard.components.DashboardDrilldownConfig.chooseDestinationDashboard": "対象ダッシュボードを選択", "xpack.dashboard.components.DashboardDrilldownConfig.openInNewTab": "新しいタブでダッシュボードを開く", @@ -39407,4 +39396,4 @@ "xpack.painlessLab.title": "Painless Lab", "xpack.painlessLab.walkthroughButtonLabel": "実地検証" } -} \ No newline at end of file +} diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 0bba12b9495e1..fd0126b228758 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -1114,9 +1114,6 @@ "dashboard.panelStorageError.setError": "设置未保存更改时遇到错误:{message}", "dashboard.share.defaultDashboardTitle": "仪表板 [{date}]", "dashboard.strings.dashboardEditTitle": "正在编辑 {title}", - "dashboard.topNav.cloneModal.dashboardExistsDescription": "单击“{confirmClone}”以克隆具有重复标题的仪表板。", - "dashboard.topNav.cloneModal.dashboardExistsTitle": "具有标题 {newDashboardName} 的仪表板已存在。", - "dashboard.topNav.showCloneModal.dashboardCopyTitle": "{title} 副本", "dashboard.actions.DownloadCreateDrilldownAction.displayName": "下载为 CSV", "dashboard.actions.downloadOptionsUnsavedFilename": "未命名", "dashboard.actions.toggleExpandPanelMenuItem.expandedDisplayName": "最小化", @@ -1128,7 +1125,6 @@ "dashboard.appLeaveConfirmModal.unsavedChangesTitle": "未保存的更改", "dashboard.badge.readOnly.text": "只读", "dashboard.badge.readOnly.tooltip": "无法保存仪表板", - "dashboard.cloneModal.cloneDashboardTitleAriaLabel": "克隆仪表板标题", "dashboard.createConfirmModal.cancelButtonLabel": "取消", "dashboard.createConfirmModal.confirmButtonLabel": "重头开始", "dashboard.createConfirmModal.continueButtonLabel": "继续编辑", @@ -1221,11 +1217,6 @@ "dashboard.solutionToolbar.addPanelButtonLabel": "创建可视化", "dashboard.solutionToolbar.editorMenuButtonLabel": "选择类型", "dashboard.solutionToolbar.quickCreateButtonGroupLegend": "常用可视化类型的快捷键", - "dashboard.topNav.cloneModal.cancelButtonLabel": "取消", - "dashboard.topNav.cloneModal.cloneDashboardModalHeaderTitle": "克隆仪表板", - "dashboard.topNav.cloneModal.confirmButtonLabel": "确认克隆", - "dashboard.topNav.cloneModal.confirmCloneDescription": "确认克隆", - "dashboard.topNav.cloneModal.enterNewNameForDashboardDescription": "请为您的仪表板输入新的名称。", "dashboard.topNav.labsButtonAriaLabel": "实验", "dashboard.topNav.labsConfigDescription": "实验", "dashboard.topNav.saveModal.objectType": "仪表板", @@ -5682,7 +5673,6 @@ "unifiedHistogram.lensTitle": "编辑可视化", "unifiedHistogram.resetChartHeight": "重置为默认高度", "unifiedHistogram.showChart": "显示图表", - "unifiedHistogram.suggestionSelectorLabel": "可视化", "unifiedHistogram.suggestionSelectorPlaceholder": "选择可视化", "unifiedHistogram.timeIntervals": "时间间隔", "unifiedHistogram.timeIntervalWithValueWarning": "警告", @@ -39297,7 +39287,6 @@ "xpack.cloudDataMigration.upgrade.text": "更轻松地升级到较新版本", "xpack.cloudLinks.deploymentLinkLabel": "管理此部署", "xpack.cloudLinks.setupGuide": "设置指南", - "xpack.cloudLinks.userMenuLinks.accountLinkText": "帐户和帐单", "xpack.cloudLinks.userMenuLinks.profileLinkText": "编辑配置文件", "xpack.dashboard.components.DashboardDrilldownConfig.chooseDestinationDashboard": "选择目标仪表板", "xpack.dashboard.components.DashboardDrilldownConfig.openInNewTab": "在新选项卡中打开仪表板", @@ -39401,4 +39390,4 @@ "xpack.painlessLab.title": "Painless 实验室", "xpack.painlessLab.walkthroughButtonLabel": "指导" } -} \ No newline at end of file +} diff --git a/x-pack/test/alerting_api_integration/common/config.ts b/x-pack/test/alerting_api_integration/common/config.ts index f77542dc09586..851bc653d6252 100644 --- a/x-pack/test/alerting_api_integration/common/config.ts +++ b/x-pack/test/alerting_api_integration/common/config.ts @@ -185,6 +185,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) '--xpack.alerting.healthCheck.interval="1s"', '--xpack.alerting.rules.minimumScheduleInterval.value="1s"', '--xpack.alerting.rules.run.alerts.max=20', + '--xpack.observability.unsafe.thresholdRule.enabled=true', `--xpack.alerting.rules.run.actions.connectorTypeOverrides=${JSON.stringify([ { id: 'test.capped', max: '1' }, ])}`, diff --git a/x-pack/test/alerting_api_integration/observability/helpers/alerting_api_helper.ts b/x-pack/test/alerting_api_integration/observability/helpers/alerting_api_helper.ts index d89908b9bef72..a50e1b4e85c14 100644 --- a/x-pack/test/alerting_api_integration/observability/helpers/alerting_api_helper.ts +++ b/x-pack/test/alerting_api_integration/observability/helpers/alerting_api_helper.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { InfraRuleType, InfraRuleTypeParams } from '@kbn/infra-plugin/common/alerting/metrics'; +import { MetricThresholdParams } from '@kbn/infra-plugin/common/alerting/metrics'; +import { ThresholdParams } from '@kbn/observability-plugin/common/threshold_rule/types'; import type { SuperTest, Test } from 'supertest'; export async function createIndexConnector({ @@ -31,31 +32,35 @@ export async function createIndexConnector({ return body.id as string; } -export async function createMetricThresholdRule({ +export async function createRule({ supertest, name, ruleTypeId, params, actions = [], + tags = [], schedule, + consumer, }: { supertest: SuperTest; - ruleTypeId: T; + ruleTypeId: string; name: string; - params: InfraRuleTypeParams[T]; + params: MetricThresholdParams | ThresholdParams; actions?: any[]; + tags?: any[]; schedule?: { interval: string }; + consumer: string; }) { const { body } = await supertest .post(`/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send({ params, - consumer: 'infrastructure', + consumer, schedule: schedule || { interval: '5m', }, - tags: ['infrastructure'], + tags, name, rule_type_id: ruleTypeId, actions, diff --git a/x-pack/test/alerting_api_integration/observability/helpers/data_view.ts b/x-pack/test/alerting_api_integration/observability/helpers/data_view.ts new file mode 100644 index 0000000000000..e8a03df2d0713 --- /dev/null +++ b/x-pack/test/alerting_api_integration/observability/helpers/data_view.ts @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SuperTest, Test } from 'supertest'; + +export const createDataView = async ({ + supertest, + id, + name, + title, +}: { + supertest: SuperTest; + id: string; + name: string; + title: string; +}) => { + const { body } = await supertest + .post(`/api/content_management/rpc/create`) + .set('kbn-xsrf', 'foo') + .send({ + contentTypeId: 'index-pattern', + data: { + fieldAttrs: '{}', + title, + timeFieldName: '@timestamp', + sourceFilters: '[]', + fields: '[]', + fieldFormatMap: '{}', + typeMeta: '{}', + runtimeFieldMap: '{}', + name, + }, + options: { id }, + version: 1, + }); + return body; +}; +export const deleteDataView = async ({ + supertest, + id, +}: { + supertest: SuperTest; + id: string; +}) => { + const { body } = await supertest + .delete(`/api/content_management/rpc/create`) + .set('kbn-xsrf', 'foo') + .send({ + contentTypeId: 'index-pattern', + id, + options: { force: true }, + version: 1, + }); + return body; +}; diff --git a/x-pack/test/alerting_api_integration/observability/helpers/syntrace.ts b/x-pack/test/alerting_api_integration/observability/helpers/syntrace.ts new file mode 100644 index 0000000000000..259924e80d64d --- /dev/null +++ b/x-pack/test/alerting_api_integration/observability/helpers/syntrace.ts @@ -0,0 +1,159 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Client } from '@elastic/elasticsearch'; +import { + ApmSynthtraceEsClient, + ApmSynthtraceKibanaClient, + createLogger, + LogLevel, +} from '@kbn/apm-synthtrace'; +import { apm, timerange } from '@kbn/apm-synthtrace-client'; + +export const getSyntraceClient = async ({ + kibanaUrl, + esClient, +}: { + kibanaUrl: string; + esClient: Client; +}) => { + const kibanaClient = new ApmSynthtraceKibanaClient({ + logger: createLogger(LogLevel.info), + target: kibanaUrl, + }); + const packageVersion = await kibanaClient.fetchLatestApmPackageVersion(); + return new ApmSynthtraceEsClient({ + client: esClient, + logger: createLogger(LogLevel.info), + refreshAfterIndex: true, + version: packageVersion, + }); +}; + +export const dataConfig = { + rate: 10, + transaction: { + name: 'GET /data', + duration: 1000, + }, + service: { + name: 'lambda-python-dev-hello', + version: '$LATEST', + runtime: { + name: 'AWS_Lambda_python3.8', + version: '3.8.11', + }, + framework: 'AWS Lambda', + agent: { + name: 'python', + version: '6.6.0', + }, + }, + containerOs: 'linux', + serverless: { + firstFunctionName: 'my-function-1', + secondFunctionName: 'my-function-2', + faasTriggerType: 'other', + }, + cloud: { + provider: 'aws', + availabilityZone: 'us-central1-c', + region: 'us-east-1', + machineType: 'e2-standard-4', + projectName: 'elastic-observability', + serviceName: 'lambda', + }, +}; + +export async function generateData({ + synthtraceEsClient, + start, + end, +}: { + synthtraceEsClient: ApmSynthtraceEsClient; + start: number; + end: number; +}) { + const { rate, service, containerOs, serverless, cloud, transaction } = dataConfig; + const { + provider, + availabilityZone, + region, + machineType, + projectName, + serviceName: cloudServiceName, + } = cloud; + const { faasTriggerType, firstFunctionName, secondFunctionName } = serverless; + const { version, runtime, framework, agent, name: serviceName } = service; + const { name: serviceRunTimeName, version: serviceRunTimeVersion } = runtime; + const { name: agentName, version: agentVersion } = agent; + + const instance = apm + .service({ name: serviceName, environment: 'production', agentName }) + .instance('instance-a'); + + const traceEvents = [ + timerange(start, end) + .interval('30s') + .rate(rate) + .generator((timestamp) => + instance + .containerId('instance-a') + .transaction({ transactionName: transaction.name }) + .timestamp(timestamp) + .defaults({ + 'cloud.provider': provider, + 'cloud.project.name': projectName, + 'cloud.service.name': cloudServiceName, + 'cloud.availability_zone': availabilityZone, + 'cloud.machine.type': machineType, + 'cloud.region': region, + 'faas.id': `arn:aws:lambda:us-west-2:123456789012:function:${firstFunctionName}`, + 'faas.trigger.type': faasTriggerType, + 'host.os.platform': containerOs, + 'kubernetes.pod.uid': '48f4c5a5-0625-4bea-9d94-77ee94a17e70', + 'service.version': version, + 'service.runtime.name': serviceRunTimeName, + 'service.runtime.version': serviceRunTimeVersion, + 'service.framework.name': framework, + 'agent.version': agentVersion, + }) + .duration(transaction.duration) + .success() + ), + + timerange(start, end) + .interval('30s') + .rate(rate) + .generator((timestamp) => + instance + .transaction({ transactionName: transaction.name }) + .timestamp(timestamp) + .defaults({ + 'cloud.provider': provider, + 'cloud.project.name': projectName, + 'cloud.service.name': cloudServiceName, + 'cloud.availability_zone': availabilityZone, + 'cloud.machine.type': machineType, + 'cloud.region': region, + 'faas.id': `arn:aws:lambda:us-west-2:123456789012:function:${secondFunctionName}`, + 'faas.trigger.type': faasTriggerType, + 'host.os.platform': containerOs, + 'kubernetes.pod.uid': '48f4c5a5-0625-4bea-9d94-77ee94a17e70', + 'service.version': version, + 'service.runtime.name': serviceRunTimeName, + 'service.runtime.version': serviceRunTimeVersion, + 'service.framework.name': framework, + 'agent.version': agentVersion, + }) + .duration(transaction.duration) + .success() + ), + ]; + + await synthtraceEsClient.index(traceEvents); +} diff --git a/x-pack/test/alerting_api_integration/observability/index.ts b/x-pack/test/alerting_api_integration/observability/index.ts index 675119da9192d..ad137560c9036 100644 --- a/x-pack/test/alerting_api_integration/observability/index.ts +++ b/x-pack/test/alerting_api_integration/observability/index.ts @@ -9,5 +9,6 @@ export default function ({ loadTestFile }: any) { describe('MetricsUI Endpoints', () => { loadTestFile(require.resolve('./metric_threshold_rule')); + loadTestFile(require.resolve('./threshold_rule')); }); } diff --git a/x-pack/test/alerting_api_integration/observability/metric_threshold_rule.ts b/x-pack/test/alerting_api_integration/observability/metric_threshold_rule.ts index d99437eae68e1..2b727820ade70 100644 --- a/x-pack/test/alerting_api_integration/observability/metric_threshold_rule.ts +++ b/x-pack/test/alerting_api_integration/observability/metric_threshold_rule.ts @@ -15,7 +15,7 @@ import { waitForRuleStatus, } from './helpers/alerting_wait_for_helpers'; import { FtrProviderContext } from '../common/ftr_provider_context'; -import { createIndexConnector, createMetricThresholdRule } from './helpers/alerting_api_helper'; +import { createIndexConnector, createRule } from './helpers/alerting_api_helper'; // eslint-disable-next-line import/no-default-export export default function ({ getService }: FtrProviderContext) { @@ -42,9 +42,11 @@ export default function ({ getService }: FtrProviderContext) { name: 'Index Connector: Metric threshold API test', indexName: ALERT_ACTION_INDEX, }); - const createdRule = await createMetricThresholdRule({ + const createdRule = await createRule({ supertest, ruleTypeId: InfraRuleType.MetricThreshold, + consumer: 'infrastructure', + tags: ['infrastructure'], name: 'Metric threshold rule', params: { criteria: [ diff --git a/x-pack/test/alerting_api_integration/observability/threshold_rule.ts b/x-pack/test/alerting_api_integration/observability/threshold_rule.ts new file mode 100644 index 0000000000000..ab1907f951745 --- /dev/null +++ b/x-pack/test/alerting_api_integration/observability/threshold_rule.ts @@ -0,0 +1,195 @@ +/* + * Copyright 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. + */ +/* + * Copyright 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 { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import { format } from 'url'; +import { Aggregators, Comparator } from '@kbn/observability-plugin/common/threshold_rule/types'; +import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/threshold/threshold_executor'; +import expect from '@kbn/expect'; +import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/observability-plugin/common/constants'; +import { FtrProviderContext } from '../common/ftr_provider_context'; +import { createIndexConnector, createRule } from './helpers/alerting_api_helper'; +import { createDataView, deleteDataView } from './helpers/data_view'; +import { getSyntraceClient, generateData } from './helpers/syntrace'; +import { waitForAlertInIndex, waitForRuleStatus } from './helpers/alerting_wait_for_helpers'; + +// eslint-disable-next-line import/no-default-export +export default function ({ getService }: FtrProviderContext) { + const start = moment(Date.now()).subtract(10, 'minutes').valueOf(); + const end = moment(Date.now()).valueOf(); + const esClient = getService('es'); + const config = getService('config'); + const kibanaServerConfig = config.get('servers.kibana'); + const kibanaUrl = format(kibanaServerConfig); + const supertest = getService('supertest'); + + describe('Threshold rule', () => { + const THRESHOLD_RULE_ALERT_INDEX = '.alerts-observability.threshold.alerts-default'; + const ALERT_ACTION_INDEX = 'alert-action-threshold'; + const DATA_VIEW_ID = 'data-view-id'; + + let synthtraceEsClient: ApmSynthtraceEsClient; + let actionId: string; + let ruleId: string; + + before(async () => { + synthtraceEsClient = await getSyntraceClient({ esClient, kibanaUrl }); + await generateData({ synthtraceEsClient, start, end }); + await createDataView({ + supertest, + name: 'test-data-view', + id: DATA_VIEW_ID, + title: 'traces-apm*,metrics-apm*,logs-apm*', + }); + }); + + after(async () => { + await supertest.delete(`/api/alerting/rule/${ruleId}`).set('kbn-xsrf', 'foo'); + await supertest.delete(`/api/actions/connector/${actionId}`).set('kbn-xsrf', 'foo'); + await esClient.deleteByQuery({ + index: THRESHOLD_RULE_ALERT_INDEX, + query: { term: { 'kibana.alert.rule.uuid': ruleId } }, + }); + await esClient.deleteByQuery({ + index: '.kibana-event-log-*', + query: { term: { 'kibana.alert.rule.consumer': 'alerts' } }, + }); + await synthtraceEsClient.clean(); + await deleteDataView({ + supertest, + id: DATA_VIEW_ID, + }); + }); + + describe('Rule creation', () => { + it('creates rule successfully', async () => { + actionId = await createIndexConnector({ + supertest, + name: 'Index Connector: Threshold API test', + indexName: ALERT_ACTION_INDEX, + }); + + const createdRule = await createRule({ + supertest, + tags: ['observability'], + consumer: 'alerts', + name: 'Threshold rule', + ruleTypeId: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, + params: { + criteria: [ + { + aggType: Aggregators.CUSTOM, + comparator: Comparator.GT, + threshold: [7500000], + timeSize: 5, + timeUnit: 'm', + customMetrics: [ + { name: 'A', field: 'span.self_time.sum.us', aggType: Aggregators.AVERAGE }, + ], + }, + ], + alertOnNoData: true, + alertOnGroupDisappear: true, + searchConfiguration: { + query: { + query: '', + language: 'kuery', + }, + index: DATA_VIEW_ID, + }, + }, + actions: [ + { + group: FIRED_ACTIONS_ID, + id: actionId, + params: { + documents: [ + { + ruleType: '{{rule.type}}', + }, + ], + }, + frequency: { + notify_when: 'onActionGroupChange', + throttle: null, + summary: false, + }, + }, + ], + }); + ruleId = createdRule.id; + expect(ruleId).not.to.be(undefined); + }); + + it('should be active', async () => { + const executionStatus = await waitForRuleStatus({ + id: ruleId, + expectedStatus: 'active', + supertest, + }); + expect(executionStatus.status).to.be('active'); + }); + + it('should set correct information in the alert document', async () => { + const resp = await waitForAlertInIndex({ + esClient, + indexName: THRESHOLD_RULE_ALERT_INDEX, + ruleId, + }); + + expect(resp.hits.hits[0]._source).property( + 'kibana.alert.rule.category', + 'Threshold (Technical Preview)' + ); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.consumer', 'alerts'); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.name', 'Threshold rule'); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.producer', 'observability'); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.revision', 0); + expect(resp.hits.hits[0]._source).property( + 'kibana.alert.rule.rule_type_id', + 'observability.rules.threshold' + ); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.uuid', ruleId); + expect(resp.hits.hits[0]._source).property('kibana.space_ids').contain('default'); + expect(resp.hits.hits[0]._source) + .property('kibana.alert.rule.tags') + .contain('observability'); + expect(resp.hits.hits[0]._source).property('kibana.alert.action_group', 'threshold.fired'); + expect(resp.hits.hits[0]._source).property('tags').contain('observability'); + expect(resp.hits.hits[0]._source).property('kibana.alert.instance.id', '*'); + expect(resp.hits.hits[0]._source).property('kibana.alert.workflow_status', 'open'); + expect(resp.hits.hits[0]._source).property('event.kind', 'signal'); + expect(resp.hits.hits[0]._source).property('event.action', 'open'); + + expect(resp.hits.hits[0]._source) + .property('kibana.alert.rule.parameters') + .eql({ + criteria: [ + { + aggType: 'custom', + comparator: '>', + threshold: [7500000], + timeSize: 5, + timeUnit: 'm', + customMetrics: [{ name: 'A', field: 'span.self_time.sum.us', aggType: 'avg' }], + }, + ], + alertOnNoData: true, + alertOnGroupDisappear: true, + searchConfiguration: { index: 'data-view-id', query: { query: '', language: 'kuery' } }, + }); + }); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/check_registered_rule_types.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/check_registered_rule_types.ts index cb74808e3d63b..40197f1e18783 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/check_registered_rule_types.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/check_registered_rule_types.ts @@ -63,6 +63,7 @@ export default function createRegisteredRuleTypeTests({ getService }: FtrProvide 'monitoring_alert_elasticsearch_version_mismatch', 'monitoring_ccr_read_exceptions', 'monitoring_shard_size', + 'observability.rules.threshold', 'apm.transaction_duration', 'apm.anomaly', 'apm.error_rate', diff --git a/x-pack/test/api_integration/apis/cloud_security_posture/benchmark.ts b/x-pack/test/api_integration/apis/cloud_security_posture/benchmark.ts index 997ff9c636f0a..0c3382dc8521b 100644 --- a/x-pack/test/api_integration/apis/cloud_security_posture/benchmark.ts +++ b/x-pack/test/api_integration/apis/cloud_security_posture/benchmark.ts @@ -5,8 +5,9 @@ * 2.0. */ import expect from '@kbn/expect'; -import type { BenchmarkResponse } from '@kbn/cloud-security-posture-plugin/common/types'; +import type { GetBenchmarkResponse } from '@kbn/cloud-security-posture-plugin/common/types'; import type { SuperTest, Test } from 'supertest'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { @@ -91,8 +92,9 @@ export default function ({ getService }: FtrProviderContext) { }); it(`Should return non-empty array filled with Rules if user has CSP integrations`, async () => { - const { body: res }: { body: BenchmarkResponse } = await supertest + const { body: res }: { body: GetBenchmarkResponse } = await supertest .get(`/internal/cloud_security_posture/benchmarks`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set('kbn-xsrf', 'xxxx') .expect(200); @@ -101,8 +103,9 @@ export default function ({ getService }: FtrProviderContext) { }); it(`Should return array size 2 when we set per page to be only 2 (total element is still 3)`, async () => { - const { body: res }: { body: BenchmarkResponse } = await supertest + const { body: res }: { body: GetBenchmarkResponse } = await supertest .get(`/internal/cloud_security_posture/benchmarks?per_page=2`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set('kbn-xsrf', 'xxxx') .expect(200); @@ -111,8 +114,9 @@ export default function ({ getService }: FtrProviderContext) { }); it(`Should return array size 2 when we set per page to be only 2 (total element is still 3)`, async () => { - const { body: res }: { body: BenchmarkResponse } = await supertest + const { body: res }: { body: GetBenchmarkResponse } = await supertest .get(`/internal/cloud_security_posture/benchmarks?per_page=2&page=2`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set('kbn-xsrf', 'xxxx') .expect(200); @@ -121,8 +125,9 @@ export default function ({ getService }: FtrProviderContext) { }); it(`Should return empty array when we set page to be above the last page number`, async () => { - const { body: res }: { body: BenchmarkResponse } = await supertest + const { body: res }: { body: GetBenchmarkResponse } = await supertest .get(`/internal/cloud_security_posture/benchmarks?per_page=2&page=3`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set('kbn-xsrf', 'xxxx') .expect(200); diff --git a/x-pack/test/api_integration/apis/cloud_security_posture/status/status_index_timeout.ts b/x-pack/test/api_integration/apis/cloud_security_posture/status/status_index_timeout.ts index eae7154763bc0..fe52d8d3a0773 100644 --- a/x-pack/test/api_integration/apis/cloud_security_posture/status/status_index_timeout.ts +++ b/x-pack/test/api_integration/apis/cloud_security_posture/status/status_index_timeout.ts @@ -6,6 +6,7 @@ */ import expect from '@kbn/expect'; import type { CspSetupStatus } from '@kbn/cloud-security-posture-plugin/common/types'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; import { FINDINGS_INDEX_DEFAULT_NS, LATEST_FINDINGS_INDEX_DEFAULT_NS, @@ -105,6 +106,7 @@ export default function (providerContext: FtrProviderContext) { const { body: res }: { body: CspSetupStatus } = await supertest .get(`/internal/cloud_security_posture/status`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set('kbn-xsrf', 'xxxx') .expect(200); @@ -131,6 +133,7 @@ export default function (providerContext: FtrProviderContext) { const { body: res }: { body: CspSetupStatus } = await supertest .get(`/internal/cloud_security_posture/status`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set('kbn-xsrf', 'xxxx') .expect(200); @@ -157,6 +160,7 @@ export default function (providerContext: FtrProviderContext) { const { body: res }: { body: CspSetupStatus } = await supertest .get(`/internal/cloud_security_posture/status`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set('kbn-xsrf', 'xxxx') .expect(200); diff --git a/x-pack/test/api_integration/apis/cloud_security_posture/status/status_indexed.ts b/x-pack/test/api_integration/apis/cloud_security_posture/status/status_indexed.ts index c8cd9927c72d4..aa9d6d3289e95 100644 --- a/x-pack/test/api_integration/apis/cloud_security_posture/status/status_indexed.ts +++ b/x-pack/test/api_integration/apis/cloud_security_posture/status/status_indexed.ts @@ -5,6 +5,7 @@ * 2.0. */ import expect from '@kbn/expect'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; import type { CspSetupStatus } from '@kbn/cloud-security-posture-plugin/common/types'; import { FINDINGS_INDEX_DEFAULT_NS, @@ -71,6 +72,7 @@ export default function (providerContext: FtrProviderContext) { const { body: res }: { body: CspSetupStatus } = await supertest .get(`/internal/cloud_security_posture/status`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set('kbn-xsrf', 'xxxx') .expect(200); @@ -89,6 +91,7 @@ export default function (providerContext: FtrProviderContext) { const { body: res }: { body: CspSetupStatus } = await supertest .get(`/internal/cloud_security_posture/status`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set('kbn-xsrf', 'xxxx') .expect(200); @@ -107,6 +110,7 @@ export default function (providerContext: FtrProviderContext) { const { body: res }: { body: CspSetupStatus } = await supertest .get(`/internal/cloud_security_posture/status`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set('kbn-xsrf', 'xxxx') .expect(200); diff --git a/x-pack/test/api_integration/apis/cloud_security_posture/status/status_indexing.ts b/x-pack/test/api_integration/apis/cloud_security_posture/status/status_indexing.ts index d7c11c446544a..ef16eb94d8a33 100644 --- a/x-pack/test/api_integration/apis/cloud_security_posture/status/status_indexing.ts +++ b/x-pack/test/api_integration/apis/cloud_security_posture/status/status_indexing.ts @@ -5,6 +5,7 @@ * 2.0. */ import expect from '@kbn/expect'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; import type { CspSetupStatus } from '@kbn/cloud-security-posture-plugin/common/types'; import { FINDINGS_INDEX_DEFAULT_NS, @@ -70,6 +71,7 @@ export default function (providerContext: FtrProviderContext) { const { body: res }: { body: CspSetupStatus } = await supertest .get(`/internal/cloud_security_posture/status`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set('kbn-xsrf', 'xxxx') .expect(200); @@ -88,6 +90,7 @@ export default function (providerContext: FtrProviderContext) { const { body: res }: { body: CspSetupStatus } = await supertest .get(`/internal/cloud_security_posture/status`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set('kbn-xsrf', 'xxxx') .expect(200); @@ -106,6 +109,7 @@ export default function (providerContext: FtrProviderContext) { const { body: res }: { body: CspSetupStatus } = await supertest .get(`/internal/cloud_security_posture/status`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set('kbn-xsrf', 'xxxx') .expect(200); diff --git a/x-pack/test/api_integration/apis/cloud_security_posture/status/status_not_deployed_not_installed.ts b/x-pack/test/api_integration/apis/cloud_security_posture/status/status_not_deployed_not_installed.ts index 93b8c81ad44de..d7d77c93ecad4 100644 --- a/x-pack/test/api_integration/apis/cloud_security_posture/status/status_not_deployed_not_installed.ts +++ b/x-pack/test/api_integration/apis/cloud_security_posture/status/status_not_deployed_not_installed.ts @@ -6,6 +6,7 @@ */ import expect from '@kbn/expect'; import type { CspSetupStatus } from '@kbn/cloud-security-posture-plugin/common/types'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { createPackagePolicy } from '../helper'; @@ -50,6 +51,7 @@ export default function (providerContext: FtrProviderContext) { const { body: res }: { body: CspSetupStatus } = await supertest .get(`/internal/cloud_security_posture/status`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set('kbn-xsrf', 'xxxx') .expect(200); @@ -72,6 +74,7 @@ export default function (providerContext: FtrProviderContext) { const { body: res }: { body: CspSetupStatus } = await supertest .get(`/internal/cloud_security_posture/status`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set('kbn-xsrf', 'xxxx') .expect(200); @@ -94,6 +97,7 @@ export default function (providerContext: FtrProviderContext) { const { body: res }: { body: CspSetupStatus } = await supertest .get(`/internal/cloud_security_posture/status`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set('kbn-xsrf', 'xxxx') .expect(200); diff --git a/x-pack/test/api_integration/apis/cloud_security_posture/status/status_unprivileged.ts b/x-pack/test/api_integration/apis/cloud_security_posture/status/status_unprivileged.ts index 3127519b2bc4c..2432165566de8 100644 --- a/x-pack/test/api_integration/apis/cloud_security_posture/status/status_unprivileged.ts +++ b/x-pack/test/api_integration/apis/cloud_security_posture/status/status_unprivileged.ts @@ -5,6 +5,7 @@ * 2.0. */ import expect from '@kbn/expect'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; import type { CspSetupStatus } from '@kbn/cloud-security-posture-plugin/common/types'; import { BENCHMARK_SCORE_INDEX_DEFAULT_NS, @@ -79,6 +80,7 @@ export default function (providerContext: FtrProviderContext) { const { body: res }: { body: CspSetupStatus } = await supertestWithoutAuth .get(`/internal/cloud_security_posture/status`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set('kbn-xsrf', 'xxxx') .auth(UNPRIVILEGED_USERNAME, 'changeme') .expect(200); @@ -127,6 +129,7 @@ export default function (providerContext: FtrProviderContext) { const { body: res }: { body: CspSetupStatus } = await supertestWithoutAuth .get(`/internal/cloud_security_posture/status`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set('kbn-xsrf', 'xxxx') .auth(UNPRIVILEGED_USERNAME, 'changeme') .expect(200); @@ -157,6 +160,7 @@ export default function (providerContext: FtrProviderContext) { const { body: res }: { body: CspSetupStatus } = await supertestWithoutAuth .get(`/internal/cloud_security_posture/status`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set('kbn-xsrf', 'xxxx') .auth(UNPRIVILEGED_USERNAME, 'changeme') .expect(200); @@ -190,6 +194,7 @@ export default function (providerContext: FtrProviderContext) { const { body: res }: { body: CspSetupStatus } = await supertestWithoutAuth .get(`/internal/cloud_security_posture/status`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set('kbn-xsrf', 'xxxx') .auth(UNPRIVILEGED_USERNAME, 'changeme') .expect(200); diff --git a/x-pack/test/api_integration/apis/cloud_security_posture/status/status_waiting_for_results.ts b/x-pack/test/api_integration/apis/cloud_security_posture/status/status_waiting_for_results.ts index 8153851124329..53692014f767a 100644 --- a/x-pack/test/api_integration/apis/cloud_security_posture/status/status_waiting_for_results.ts +++ b/x-pack/test/api_integration/apis/cloud_security_posture/status/status_waiting_for_results.ts @@ -5,6 +5,7 @@ * 2.0. */ import expect from '@kbn/expect'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; import type { CspSetupStatus } from '@kbn/cloud-security-posture-plugin/common/types'; import { setupFleetAndAgents } from '../../../../fleet_api_integration/apis/agents/services'; import { generateAgent } from '../../../../fleet_api_integration/helpers'; @@ -87,6 +88,7 @@ export default function (providerContext: FtrProviderContext) { const { body: res }: { body: CspSetupStatus } = await supertest .get(`/internal/cloud_security_posture/status`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set('kbn-xsrf', 'xxxx') .expect(200); expect(res.kspm.status).to.be('waiting_for_results'); @@ -112,6 +114,7 @@ export default function (providerContext: FtrProviderContext) { const { body: res }: { body: CspSetupStatus } = await supertest .get(`/internal/cloud_security_posture/status`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set('kbn-xsrf', 'xxxx') .expect(200); expect(res.cspm.status).to.be('waiting_for_results'); @@ -137,6 +140,7 @@ export default function (providerContext: FtrProviderContext) { const { body: res }: { body: CspSetupStatus } = await supertest .get(`/internal/cloud_security_posture/status`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set('kbn-xsrf', 'xxxx') .expect(200); expect(res.vuln_mgmt.status).to.be('waiting_for_results'); diff --git a/x-pack/test/api_integration/apis/ml/system/capabilities.ts b/x-pack/test/api_integration/apis/ml/system/capabilities.ts index 814ebb7fbaa21..dcfeba197026c 100644 --- a/x-pack/test/api_integration/apis/ml/system/capabilities.ts +++ b/x-pack/test/api_integration/apis/ml/system/capabilities.ts @@ -12,7 +12,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; import { getCommonRequestHeader } from '../../../../functional/services/ml/common_api'; import { USER } from '../../../../functional/services/ml/security_common'; -const NUMBER_OF_CAPABILITIES = 39; +const NUMBER_OF_CAPABILITIES = 42; export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertestWithoutAuth'); @@ -93,6 +93,9 @@ export default ({ getService }: FtrProviderContext) => { canCreateTrainedModels: false, canDeleteTrainedModels: false, canStartStopTrainedModels: false, + isADEnabled: true, + isDFAEnabled: true, + isNLPEnabled: true, }); }); @@ -139,6 +142,9 @@ export default ({ getService }: FtrProviderContext) => { canCreateTrainedModels: true, canDeleteTrainedModels: true, canStartStopTrainedModels: true, + isADEnabled: true, + isDFAEnabled: true, + isNLPEnabled: true, }); }); }); diff --git a/x-pack/test/api_integration/apis/ml/system/space_capabilities.ts b/x-pack/test/api_integration/apis/ml/system/space_capabilities.ts index c746c6d316efa..f2c62a19886a0 100644 --- a/x-pack/test/api_integration/apis/ml/system/space_capabilities.ts +++ b/x-pack/test/api_integration/apis/ml/system/space_capabilities.ts @@ -15,7 +15,7 @@ import { USER } from '../../../../functional/services/ml/security_common'; const idSpaceWithMl = 'space_with_ml'; const idSpaceNoMl = 'space_no_ml'; -const NUMBER_OF_CAPABILITIES = 39; +const NUMBER_OF_CAPABILITIES = 42; export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertestWithoutAuth'); @@ -122,6 +122,9 @@ export default ({ getService }: FtrProviderContext) => { canCreateTrainedModels: false, canDeleteTrainedModels: false, canStartStopTrainedModels: false, + isADEnabled: true, + isDFAEnabled: true, + isNLPEnabled: true, }); }); @@ -167,6 +170,9 @@ export default ({ getService }: FtrProviderContext) => { canCreateTrainedModels: false, canDeleteTrainedModels: false, canStartStopTrainedModels: false, + isADEnabled: true, + isDFAEnabled: true, + isNLPEnabled: true, }); }); @@ -212,6 +218,9 @@ export default ({ getService }: FtrProviderContext) => { canCreateTrainedModels: true, canDeleteTrainedModels: true, canStartStopTrainedModels: true, + isADEnabled: true, + isDFAEnabled: true, + isNLPEnabled: true, }); }); @@ -257,6 +266,9 @@ export default ({ getService }: FtrProviderContext) => { canCreateTrainedModels: false, canDeleteTrainedModels: false, canStartStopTrainedModels: false, + isADEnabled: true, + isDFAEnabled: true, + isNLPEnabled: true, }); }); }); diff --git a/x-pack/test/api_integration/apis/synthetics/index.ts b/x-pack/test/api_integration/apis/synthetics/index.ts index 13c581bf168b0..b00ff699ca1b6 100644 --- a/x-pack/test/api_integration/apis/synthetics/index.ts +++ b/x-pack/test/api_integration/apis/synthetics/index.ts @@ -32,5 +32,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./add_edit_params')); loadTestFile(require.resolve('./add_monitor_project_private_location')); loadTestFile(require.resolve('./inspect_monitor')); + loadTestFile(require.resolve('./test_now_monitor')); }); } diff --git a/x-pack/test/api_integration/apis/synthetics/services/synthetics_monitor_test_service.ts b/x-pack/test/api_integration/apis/synthetics/services/synthetics_monitor_test_service.ts index 7dea2de5453d7..ea465986fadc5 100644 --- a/x-pack/test/api_integration/apis/synthetics/services/synthetics_monitor_test_service.ts +++ b/x-pack/test/api_integration/apis/synthetics/services/synthetics_monitor_test_service.ts @@ -10,14 +10,17 @@ import { syntheticsMonitorType } from '@kbn/synthetics-plugin/common/types/saved import { SavedObject } from '@kbn/core-saved-objects-common/src/server_types'; import { MonitorFields } from '@kbn/synthetics-plugin/common/runtime_types'; import { MonitorInspectResponse } from '@kbn/synthetics-plugin/public/apps/synthetics/state/monitor_management/api'; +import { v4 as uuidv4 } from 'uuid'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { KibanaSupertestProvider } from '../../../../../../test/api_integration/services/supertest'; export class SyntheticsMonitorTestService { private supertest: ReturnType; + private getService: FtrProviderContext['getService']; constructor(getService: FtrProviderContext['getService']) { this.supertest = getService('supertest'); + this.getService = getService; } async getMonitor(monitorId: string, decrypted: boolean = true, space?: string) { @@ -95,4 +98,34 @@ export class SyntheticsMonitorTestService { console.error(e); } } + + async addsNewSpace() { + const username = 'admin'; + const password = `${username}-password`; + const roleName = 'uptime-role'; + const SPACE_ID = `test-space-${uuidv4()}`; + const SPACE_NAME = `test-space-name ${uuidv4()}`; + + const security = this.getService('security'); + const kibanaServer = this.getService('kibanaServer'); + + await kibanaServer.spaces.create({ id: SPACE_ID, name: SPACE_NAME }); + await security.role.create(roleName, { + kibana: [ + { + feature: { + uptime: ['all'], + }, + spaces: ['*'], + }, + ], + }); + await security.user.create(username, { + password, + roles: [roleName], + full_name: 'a kibana user', + }); + + return { username, password, SPACE_ID }; + } } diff --git a/x-pack/test/api_integration/apis/synthetics/test_now_monitor.ts b/x-pack/test/api_integration/apis/synthetics/test_now_monitor.ts new file mode 100644 index 0000000000000..400e073f8a3f3 --- /dev/null +++ b/x-pack/test/api_integration/apis/synthetics/test_now_monitor.ts @@ -0,0 +1,112 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { MonitorFields } from '@kbn/synthetics-plugin/common/runtime_types'; +import { SYNTHETICS_API_URLS } from '@kbn/synthetics-plugin/common/constants'; +import expect from '@kbn/expect'; +import { omit } from 'lodash'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { getFixtureJson } from './helper/get_fixture_json'; +import { SyntheticsMonitorTestService } from './services/synthetics_monitor_test_service'; + +export default function ({ getService }: FtrProviderContext) { + describe('RunTestManually', function () { + this.tags('skipCloud'); + + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + const monitorTestService = new SyntheticsMonitorTestService(getService); + const kibanaServer = getService('kibanaServer'); + + let newMonitor: MonitorFields; + + before(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + await supertest + .put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT) + .set('kbn-xsrf', 'true') + .expect(200); + newMonitor = getFixtureJson('http_monitor'); + }); + + it('runs test manually', async () => { + const resp = await monitorTestService.addMonitor(newMonitor); + + const res = await supertest + .get(SYNTHETICS_API_URLS.TRIGGER_MONITOR + `/${resp.id}`) + .set('kbn-xsrf', 'true') + .expect(200); + + const result = res.body; + expect(typeof result.testRunId).to.eql('string'); + expect(typeof result.configId).to.eql('string'); + expect(result.schedule).to.eql({ number: '5', unit: 'm' }); + expect(result.locations).to.eql([ + { + id: 'eu-west-01', + label: 'Europe East', + geo: { lat: 33.2343132435, lon: 73.2342343434 }, + url: 'https://example-url.com', + isServiceManaged: true, + }, + { + id: 'eu-west-02', + label: 'Europe West', + geo: { lat: 33.2343132435, lon: 73.2342343434 }, + url: 'https://example-url.com', + isServiceManaged: true, + }, + ]); + + expect(omit(result.monitor, ['id', 'config_id'])).to.eql( + omit(newMonitor, ['id', 'config_id']) + ); + }); + + it('works in non default space', async () => { + const { username, SPACE_ID, password } = await monitorTestService.addsNewSpace(); + + const resp = await supertestWithoutAuth + .post(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.SYNTHETICS_MONITORS}`) + .auth(username, password) + .set('kbn-xsrf', 'true') + .send(newMonitor) + .expect(200); + + const res = await supertest + .get(`/s/${SPACE_ID}${SYNTHETICS_API_URLS.TRIGGER_MONITOR}/${resp.body.id}`) + .set('kbn-xsrf', 'true') + .expect(200); + + const result = res.body; + expect(typeof result.testRunId).to.eql('string'); + expect(typeof result.configId).to.eql('string'); + expect(result.schedule).to.eql({ number: '5', unit: 'm' }); + expect(result.locations).to.eql([ + { + id: 'eu-west-01', + label: 'Europe East', + geo: { lat: 33.2343132435, lon: 73.2342343434 }, + url: 'https://example-url.com', + isServiceManaged: true, + }, + { + id: 'eu-west-02', + label: 'Europe West', + geo: { lat: 33.2343132435, lon: 73.2342343434 }, + url: 'https://example-url.com', + isServiceManaged: true, + }, + ]); + + expect(omit(result.monitor, ['id', 'config_id'])).to.eql( + omit(newMonitor, ['id', 'config_id']) + ); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/service_overview/instance_details.spec.ts b/x-pack/test/apm_api_integration/tests/service_overview/instance_details.spec.ts index f6e257931ba54..2782e0c4f70df 100644 --- a/x-pack/test/apm_api_integration/tests/service_overview/instance_details.spec.ts +++ b/x-pack/test/apm_api_integration/tests/service_overview/instance_details.spec.ts @@ -46,7 +46,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { } ); - registry.when( + // FLAKY: https://github.com/elastic/kibana/issues/160709 + registry.when.skip( 'Instance details when data is loaded', { config: 'basic', archives: [archiveName] }, () => { diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts index 6da8cfccf1352..5166f7b135380 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts @@ -143,6 +143,22 @@ export default ({ getService }: FtrProviderContext): void => { await deleteCases({ supertest, caseIDs: ['fake-id'], expectedHttpCode: 404 }); }); + it('unhappy path - 400s when trying to delete more than 100 cases at a time', async () => { + await deleteCases({ + supertest: supertestWithoutAuth, + caseIDs: new Array(101).fill('id'), + expectedHttpCode: 400, + }); + }); + + it('unhappy path - 400s when trying to delete 0 cases at a time', async () => { + await deleteCases({ + supertest: supertestWithoutAuth, + caseIDs: [], + expectedHttpCode: 400, + }); + }); + describe('files', () => { afterEach(async () => { await deleteAllFiles({ diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts index 72b75825ce826..f284817db1f9a 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/patch_cases.ts @@ -580,6 +580,93 @@ export default ({ getService }: FtrProviderContext): void => { }); }); + it('400s if the title an empty string', async () => { + const postedCase = await createCase(supertest, postCaseReq); + await updateCase({ + supertest, + params: { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + title: '', + }, + ], + }, + expectedHttpCode: 400, + }); + }); + + it('400s if the title is a string with empty characters', async () => { + const postedCase = await createCase(supertest, postCaseReq); + await updateCase({ + supertest, + params: { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + title: ' ', + }, + ], + }, + expectedHttpCode: 400, + }); + }); + + it('400s if the description is too long', async () => { + const longDescription = 'a'.repeat(30001); + + const postedCase = await createCase(supertest, postCaseReq); + await updateCase({ + supertest, + params: { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + description: longDescription, + }, + ], + }, + expectedHttpCode: 400, + }); + }); + + it('400s if the description an empty string', async () => { + const postedCase = await createCase(supertest, postCaseReq); + await updateCase({ + supertest, + params: { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + description: '', + }, + ], + }, + expectedHttpCode: 400, + }); + }); + + it('400s if the description is a string with empty characters', async () => { + const postedCase = await createCase(supertest, postCaseReq); + await updateCase({ + supertest, + params: { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + description: ' ', + }, + ], + }, + expectedHttpCode: 400, + }); + }); + describe('categories', async () => { it('400s when a too long category value is passed', async () => { const postedCase = await createCase(supertest, postCaseReq); @@ -632,6 +719,80 @@ export default ({ getService }: FtrProviderContext): void => { }); }); }); + + describe('tags', async () => { + it('400s when tags array is too long', async () => { + const tags = Array(201).fill('foo'); + + const postedCase = await createCase(supertest, postCaseReq); + await updateCase({ + supertest, + params: { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + tags, + }, + ], + }, + expectedHttpCode: 400, + }); + }); + + it('400s when tag string is too long', async () => { + const tag = 'a'.repeat(257); + + const postedCase = await createCase(supertest, postCaseReq); + await updateCase({ + supertest, + params: { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + tags: [tag], + }, + ], + }, + expectedHttpCode: 400, + }); + }); + + it('400s when an empty string is passed in tags', async () => { + const postedCase = await createCase(supertest, postCaseReq); + await updateCase({ + supertest, + params: { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + tags: ['', 'one'], + }, + ], + }, + expectedHttpCode: 400, + }); + }); + + it('400s when a string with spaces tag value is passed', async () => { + const postedCase = await createCase(supertest, postCaseReq); + await updateCase({ + supertest, + params: { + cases: [ + { + id: postedCase.id, + version: postedCase.version, + tags: [' '], + }, + ], + }, + expectedHttpCode: 400, + }); + }); + }); }); describe('alerts', () => { diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/post_case.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/post_case.ts index c6aea823f3bbc..f36ebc6e8961f 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/post_case.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/post_case.ts @@ -265,6 +265,12 @@ export default ({ getService }: FtrProviderContext): void => { await createCase(supertest, getPostCaseRequest({ title: longTitle }), 400); }); + it('400s if the description is too long', async () => { + const longDescription = 'a'.repeat(30001); + + await createCase(supertest, getPostCaseRequest({ description: longDescription }), 400); + }); + describe('tags', async () => { it('400s if the a tag is a whitespace', async () => { const tags = ['test', ' ']; @@ -277,6 +283,18 @@ export default ({ getService }: FtrProviderContext): void => { await createCase(supertest, getPostCaseRequest({ tags }), 400); }); + + it('400s if the a tag is too long', async () => { + const tag = 'a'.repeat(257); + + await createCase(supertest, getPostCaseRequest({ tags: [tag] }), 400); + }); + + it('400s if the a tags array is too long', async () => { + const tags = Array(201).fill('foo'); + + await createCase(supertest, getPostCaseRequest({ tags }), 400); + }); }); describe('categories', async () => { diff --git a/x-pack/test/cloud_security_posture_api/telemetry/telemetry.ts b/x-pack/test/cloud_security_posture_api/telemetry/telemetry.ts index ede036d93239f..797a88881a87b 100644 --- a/x-pack/test/cloud_security_posture_api/telemetry/telemetry.ts +++ b/x-pack/test/cloud_security_posture_api/telemetry/telemetry.ts @@ -6,6 +6,7 @@ */ import expect from '@kbn/expect'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; import { data, MockTelemetryFindings } from './data'; import type { FtrProviderContext } from '../ftr_provider_context'; @@ -26,6 +27,7 @@ export default function ({ getService }: FtrProviderContext) { log.debug('Check CSP plugin is initialized'); const response = await supertest .get('/internal/cloud_security_posture/status?check=init') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .expect(200); expect(response.body).to.eql({ isPluginInitialized: true }); log.debug('CSP plugin is initialized'); diff --git a/x-pack/test/cloud_security_posture_functional/page_objects/csp_dashboard_page.ts b/x-pack/test/cloud_security_posture_functional/page_objects/csp_dashboard_page.ts index 635cf88965e5f..c7265ece4744a 100644 --- a/x-pack/test/cloud_security_posture_functional/page_objects/csp_dashboard_page.ts +++ b/x-pack/test/cloud_security_posture_functional/page_objects/csp_dashboard_page.ts @@ -6,6 +6,7 @@ */ import expect from '@kbn/expect'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; import type { FtrProviderContext } from '../ftr_provider_context'; // Defined in CSP plugin @@ -27,6 +28,7 @@ export function CspDashboardPageProvider({ getService, getPageObjects }: FtrProv log.debug('Check CSP plugin is initialized'); const response = await supertest .get('/internal/cloud_security_posture/status?check=init') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .expect(200); expect(response.body).to.eql({ isPluginInitialized: true }); log.debug('CSP plugin is initialized'); diff --git a/x-pack/test/cloud_security_posture_functional/page_objects/findings_page.ts b/x-pack/test/cloud_security_posture_functional/page_objects/findings_page.ts index 6bb3a78e44cb4..59ec355ca770f 100644 --- a/x-pack/test/cloud_security_posture_functional/page_objects/findings_page.ts +++ b/x-pack/test/cloud_security_posture_functional/page_objects/findings_page.ts @@ -6,6 +6,7 @@ */ import expect from '@kbn/expect'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; import type { FtrProviderContext } from '../ftr_provider_context'; // Defined in CSP plugin @@ -28,6 +29,7 @@ export function FindingsPageProvider({ getService, getPageObjects }: FtrProvider log.debug('Check CSP plugin is initialized'); const response = await supertest .get('/internal/cloud_security_posture/status?check=init') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') .expect(200); expect(response.body).to.eql({ isPluginInitialized: true }); log.debug('CSP plugin is initialized'); diff --git a/x-pack/test/fleet_api_integration/apis/agents/uploads.ts b/x-pack/test/fleet_api_integration/apis/agents/uploads.ts index 6f548d3d955d0..174d511674ef6 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/uploads.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/uploads.ts @@ -23,6 +23,25 @@ export default function (providerContext: FtrProviderContext) { const ES_INDEX_OPTIONS = { headers: { 'X-elastic-product-origin': 'fleet' } }; + const cleanupFiles = async () => { + await esClient.deleteByQuery({ + index: `${FILE_STORAGE_DATA_AGENT_INDEX},${FILE_STORAGE_METADATA_AGENT_INDEX}`, + refresh: true, + ignore_unavailable: true, + query: { + bool: { + filter: [ + { + ids: { + values: ['file1', 'file1.0'], + }, + }, + ], + }, + }, + }); + }; + describe('fleet_uploads', () => { skipIfNoDockerRegistry(providerContext); setupFleetAndAgents(providerContext); @@ -30,6 +49,7 @@ export default function (providerContext: FtrProviderContext) { before(async () => { await esArchiver.unload('x-pack/test/functional/es_archives/fleet/empty_fleet_server'); await getService('supertest').post(`/api/fleet/setup`).set('kbn-xsrf', 'xxx').send(); + await cleanupFiles(); await esClient.create({ index: AGENT_ACTIONS_INDEX, @@ -60,34 +80,36 @@ export default function (providerContext: FtrProviderContext) { ES_INDEX_OPTIONS ); - await esClient.update({ + await esClient.index({ index: FILE_STORAGE_METADATA_AGENT_INDEX, id: 'file1', refresh: true, + op_type: 'create', body: { - doc_as_upsert: true, - doc: { - upload_id: 'file1', - action_id: 'action1', - agent_id: 'agent1', - file: { - ChunkSize: 4194304, - extension: 'zip', - hash: {}, - mime_type: 'application/zip', - mode: '0644', - name: 'elastic-agent-diagnostics-2022-10-07T12-00-00Z-00.zip', - path: '/agent/elastic-agent-diagnostics-2022-10-07T12-00-00Z-00.zip', - size: 24917, - Status: 'READY', - type: 'file', - }, + '@timestamp': new Date().toISOString(), + upload_id: 'file1', + action_id: 'action1', + agent_id: 'agent1', + file: { + ChunkSize: 4194304, + extension: 'zip', + hash: {}, + mime_type: 'application/zip', + mode: '0644', + name: 'elastic-agent-diagnostics-2022-10-07T12-00-00Z-00.zip', + path: '/agent/elastic-agent-diagnostics-2022-10-07T12-00-00Z-00.zip', + size: 24917, + Status: 'READY', + type: 'file', }, }, }); }); after(async () => { - await esArchiver.load('x-pack/test/functional/es_archives/fleet/empty_fleet_server'); + await Promise.all([ + esArchiver.load('x-pack/test/functional/es_archives/fleet/empty_fleet_server'), + cleanupFiles(), + ]); }); it('should get agent uploads', async () => { @@ -108,17 +130,16 @@ export default function (providerContext: FtrProviderContext) { }); it('should get agent uploaded file', async () => { - await esClient.update({ + await esClient.index({ index: FILE_STORAGE_DATA_AGENT_INDEX, id: 'file1.0', + op_type: 'create', refresh: true, body: { - doc_as_upsert: true, - doc: { - last: true, - bid: 'file1', - data: 'test', - }, + '@timestamp': new Date().toISOString(), + last: true, + bid: 'file1', + data: 'test', }, }); diff --git a/x-pack/test/functional/apps/discover/visualize_field.ts b/x-pack/test/functional/apps/discover/visualize_field.ts index bb914c45fa2b7..22049dc351caa 100644 --- a/x-pack/test/functional/apps/discover/visualize_field.ts +++ b/x-pack/test/functional/apps/discover/visualize_field.ts @@ -156,11 +156,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await testSubjects.click('querySubmitButton'); await PageObjects.header.waitUntilLoadingHasFinished(); await testSubjects.click('TextBasedLangEditor-expand'); - await testSubjects.click('unifiedHistogramEditVisualization'); + await testSubjects.click('unifiedHistogramEditFlyoutVisualization'); await PageObjects.header.waitUntilLoadingHasFinished(); - await retry.waitFor('lens visualization', async () => { + await retry.waitFor('lens flyout', async () => { const dimensions = await testSubjects.findAll('lns-dimensionTrigger-textBased'); return dimensions.length === 2 && (await dimensions[1].getVisibleText()) === 'average'; }); @@ -175,11 +175,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await testSubjects.click('querySubmitButton'); await PageObjects.header.waitUntilLoadingHasFinished(); await testSubjects.click('TextBasedLangEditor-expand'); - await testSubjects.click('unifiedHistogramEditVisualization'); + await testSubjects.click('unifiedHistogramEditFlyoutVisualization'); await PageObjects.header.waitUntilLoadingHasFinished(); - await retry.waitFor('lens visualization', async () => { + await retry.waitFor('lens flyout', async () => { const dimensions = await testSubjects.findAll('lns-dimensionTrigger-textBased'); return dimensions.length === 2 && (await dimensions[1].getVisibleText()) === 'average'; }); diff --git a/x-pack/test/functional_cloud/config.ts b/x-pack/test/functional_cloud/config.ts index bfcb34d15de43..c3203677631a9 100644 --- a/x-pack/test/functional_cloud/config.ts +++ b/x-pack/test/functional_cloud/config.ts @@ -48,6 +48,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { '--xpack.cloud.base_url=https://cloud.elastic.co', '--xpack.cloud.deployment_url=/deployments/deploymentId', '--xpack.cloud.organization_url=/organization/organizationId', + '--xpack.cloud.billing_url=/billing', '--xpack.cloud.profile_url=/user/userId', '--xpack.security.authc.selector.enabled=false', `--xpack.security.authc.providers=${JSON.stringify({ diff --git a/x-pack/test/functional_cloud/tests/cloud_links.ts b/x-pack/test/functional_cloud/tests/cloud_links.ts index 0d5ab43837b23..743df23740fb4 100644 --- a/x-pack/test/functional_cloud/tests/cloud_links.ts +++ b/x-pack/test/functional_cloud/tests/cloud_links.ts @@ -55,18 +55,21 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); describe('Fills up the user menu items', () => { - it('Shows the button Edit profile', async () => { - await PageObjects.common.clickAndValidate('userMenuButton', 'userMenuLink__Edit profile'); - const cloudLink = await find.byLinkText('Edit profile'); + it('Shows the button Profile', async () => { + await PageObjects.common.clickAndValidate('userMenuButton', 'userMenuLink__Profile'); + const cloudLink = await find.byLinkText('Profile'); expect(cloudLink).to.not.be(null); }); - it('Shows the button Account & Billing', async () => { - await PageObjects.common.clickAndValidate( - 'userMenuButton', - 'userMenuLink__Account & Billing' - ); - const cloudLink = await find.byLinkText('Account & Billing'); + it('Shows the button Billing', async () => { + await PageObjects.common.clickAndValidate('userMenuButton', 'userMenuLink__Billing'); + const cloudLink = await find.byLinkText('Billing'); + expect(cloudLink).to.not.be(null); + }); + + it('Shows the button Organization', async () => { + await PageObjects.common.clickAndValidate('userMenuButton', 'userMenuLink__Organization'); + const cloudLink = await find.byLinkText('Organization'); expect(cloudLink).to.not.be(null); }); }); diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/file_upload_index.ts b/x-pack/test/security_solution_endpoint_api_int/apis/file_upload_index.ts deleted file mode 100644 index ce8179a050c47..0000000000000 --- a/x-pack/test/security_solution_endpoint_api_int/apis/file_upload_index.ts +++ /dev/null @@ -1,34 +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 expect from '@kbn/expect'; -import { - FILE_STORAGE_DATA_INDEX, - FILE_STORAGE_METADATA_INDEX, -} from '@kbn/security-solution-plugin/common/endpoint/constants'; -import { FtrProviderContext } from '../ftr_provider_context'; - -export default function ({ getService }: FtrProviderContext) { - const esClient = getService('es'); - - describe('File upload indices', () => { - it('should have created the file data index on install', async () => { - const endpointFileUploadIndexExists = await esClient.indices.exists({ - index: FILE_STORAGE_METADATA_INDEX, - }); - - expect(endpointFileUploadIndexExists).equal(true); - }); - it('should have created the files index on install', async () => { - const endpointFileUploadIndexExists = await esClient.indices.exists({ - index: FILE_STORAGE_DATA_INDEX, - }); - - expect(endpointFileUploadIndexExists).equal(true); - }); - }); -} diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/index.ts b/x-pack/test/security_solution_endpoint_api_int/apis/index.ts index fbd33d38d1a94..e68db8182da20 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/index.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/index.ts @@ -48,7 +48,6 @@ export default function endpointAPIIntegrationTests(providerContext: FtrProvider loadTestFile(require.resolve('./package')); loadTestFile(require.resolve('./endpoint_authz')); loadTestFile(require.resolve('./endpoint_response_actions/execute')); - loadTestFile(require.resolve('./file_upload_index')); loadTestFile(require.resolve('./endpoint_artifacts/trusted_apps')); loadTestFile(require.resolve('./endpoint_artifacts/event_filters')); loadTestFile(require.resolve('./endpoint_artifacts/host_isolation_exceptions')); diff --git a/yarn.lock b/yarn.lock index 7909e5466ce76..dec901f6e1b29 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4954,6 +4954,10 @@ version "0.0.0" uid "" +"@kbn/rrule@link:packages/kbn-rrule": + version "0.0.0" + uid "" + "@kbn/rule-data-utils@link:packages/kbn-rule-data-utils": version "0.0.0" uid "" @@ -5714,6 +5718,10 @@ version "0.0.0" uid "" +"@kbn/v8-profiler-examples-plugin@link:examples/v8_profiler_examples": + version "0.0.0" + uid "" + "@kbn/validate-next-docs-cli@link:packages/kbn-validate-next-docs-cli": version "0.0.0" uid "" @@ -20699,7 +20707,7 @@ lru-queue@0.1: dependencies: es5-ext "~0.10.2" -luxon@^1.21.3, luxon@^1.25.0: +luxon@^1.25.0: version "1.28.1" resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.1.tgz#528cdf3624a54506d710290a2341aa8e6e6c61b0" integrity sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw== @@ -25670,15 +25678,6 @@ robust-predicates@^3.0.0: resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-3.0.1.tgz#ecde075044f7f30118682bd9fb3f123109577f9a" integrity sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g== -rrule@2.6.4: - version "2.6.4" - resolved "https://registry.yarnpkg.com/rrule/-/rrule-2.6.4.tgz#7f4f31fda12bc7249bb176c891109a9bc448e035" - integrity sha512-sLdnh4lmjUqq8liFiOUXD5kWp/FcnbDLPwq5YAc/RrN6120XOPb86Ae5zxF7ttBVq8O3LxjjORMEit1baluahA== - dependencies: - tslib "^1.10.0" - optionalDependencies: - luxon "^1.21.3" - rst-selector-parser@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz#81b230ea2fcc6066c89e3472de794285d9b03d91"